mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-09 23:41:33 +01:00
Update crypto-primitives and their contraints.
This commit is contained in:
@@ -49,10 +49,4 @@ harness = false
|
||||
[[bench]]
|
||||
name = "schnorr_sig"
|
||||
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
|
||||
@@ -10,7 +10,7 @@ use crypto_primitives::commitment::{pedersen::*, CommitmentScheme};
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CommWindow;
|
||||
|
||||
impl PedersenWindow for CommWindow {
|
||||
impl Window for CommWindow {
|
||||
const WINDOW_SIZE: usize = 250;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
@@ -19,25 +19,21 @@ fn pedersen_comm_setup(c: &mut Criterion) {
|
||||
c.bench_function("Pedersen Commitment Setup", move |b| {
|
||||
b.iter(|| {
|
||||
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) {
|
||||
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];
|
||||
c.bench_function("Pedersen Commitment Eval", move |b| {
|
||||
b.iter(|| {
|
||||
let rng = &mut rand::thread_rng();
|
||||
let commitment_randomness = PedersenRandomness::rand(rng);
|
||||
PedersenCommitment::<Edwards, CommWindow>::commit(
|
||||
¶meters,
|
||||
&input,
|
||||
&commitment_randomness,
|
||||
)
|
||||
.unwrap();
|
||||
let commitment_randomness = Randomness::rand(rng);
|
||||
Commitment::<Edwards, CommWindow>::commit(¶meters, &input, &commitment_randomness)
|
||||
.unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crypto_primitives::crh::{pedersen::*, FixedLengthCRH};
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct HashWindow;
|
||||
|
||||
impl PedersenWindow for HashWindow {
|
||||
impl Window for HashWindow {
|
||||
const WINDOW_SIZE: usize = 250;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
@@ -19,18 +19,18 @@ fn pedersen_crh_setup(c: &mut Criterion) {
|
||||
c.bench_function("Pedersen CRH Setup", move |b| {
|
||||
b.iter(|| {
|
||||
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) {
|
||||
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];
|
||||
c.bench_function("Pedersen CRH Eval", move |b| {
|
||||
b.iter(|| {
|
||||
PedersenCRH::<Edwards, HashWindow>::evaluate(¶meters, &input).unwrap();
|
||||
CRH::<Edwards, HashWindow>::evaluate(¶meters, &input).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
¶ms.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);
|
||||
@@ -1,180 +1,89 @@
|
||||
#[macro_use]
|
||||
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};
|
||||
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("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(¶meters, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_sign(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
|
||||
c.bench_function("SchnorrEdwardsAffine: Sign", move |b| {
|
||||
b.iter(|| {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_verify(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwardsAffine: Verify", move |b| {
|
||||
b.iter(|| SchnorrEdwards::verify(¶meters, &pk, &message, &signature).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_pk(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, _) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
|
||||
c.bench_function("SchnorrEdwardsAffine: Randomize PubKey", move |b| {
|
||||
b.iter(|| SchnorrEdwards::randomize_public_key(¶meters, &pk, &randomness).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_signature(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwardsAffine: Randomize Signature", move |b| {
|
||||
b.iter(|| {
|
||||
SchnorrEdwards::randomize_signature(¶meters, &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
|
||||
}
|
||||
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};
|
||||
fn schnorr_signature_keygen(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
|
||||
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(¶meters, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_sign(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
|
||||
c.bench_function("SchnorrEdwardsProjective: Sign", move |b| {
|
||||
b.iter(|| {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_verify(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwardsProjective: Verify", move |b| {
|
||||
b.iter(|| SchnorrEdwards::verify(¶meters, &pk, &message, &signature).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_pk(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, _) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
|
||||
c.bench_function("SchnorrEdwardsProjective: Randomize PubKey", move |b| {
|
||||
b.iter(|| SchnorrEdwards::randomize_public_key(¶meters, &pk, &randomness).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_signature(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwardsProjective: Randomize Signature", move |b| {
|
||||
b.iter(|| {
|
||||
SchnorrEdwards::randomize_signature(¶meters, &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
|
||||
}
|
||||
c.bench_function("SchnorrEdwards: KeyGen", move |b| {
|
||||
b.iter(|| {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
SchnorrEdwards::keygen(¶meters, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
use crate::{affine::schnorr_sig_affine, projective::schnorr_sig_projective};
|
||||
criterion_main!(schnorr_sig_affine, schnorr_sig_projective);
|
||||
|
||||
fn schnorr_signature_sign(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
|
||||
c.bench_function("SchnorrEdwards: Sign", move |b| {
|
||||
b.iter(|| {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_verify(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwards: Verify", move |b| {
|
||||
b.iter(|| SchnorrEdwards::verify(¶meters, &pk, &message, &signature).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_pk(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (pk, _) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
|
||||
c.bench_function("SchnorrEdwards: Randomize PubKey", move |b| {
|
||||
b.iter(|| SchnorrEdwards::randomize_public_key(¶meters, &pk, &randomness).unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
fn schnorr_signature_randomize_signature(c: &mut Criterion) {
|
||||
let mut rng = &mut rand::thread_rng();
|
||||
let parameters = SchnorrEdwards::setup(&mut rng).unwrap();
|
||||
let (_, sk) = SchnorrEdwards::keygen(¶meters, &mut rng).unwrap();
|
||||
let randomness: [u8; 32] = rng.gen();
|
||||
let message = [100u8; 128];
|
||||
let signature = SchnorrEdwards::sign(¶meters, &sk, &message, &mut rng).unwrap();
|
||||
|
||||
c.bench_function("SchnorrEdwards: Randomize Signature", move |b| {
|
||||
b.iter(|| {
|
||||
SchnorrEdwards::randomize_signature(¶meters, &signature, &randomness).unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
criterion_group! {
|
||||
name = schnorr_sig;
|
||||
config = Criterion::default().sample_size(20);
|
||||
targets = schnorr_signature_setup, schnorr_signature_keygen, schnorr_signature_sign,
|
||||
schnorr_signature_verify, schnorr_signature_randomize_pk, schnorr_signature_randomize_signature
|
||||
}
|
||||
criterion_main!(schnorr_sig);
|
||||
|
||||
@@ -40,9 +40,9 @@ rayon = { version = "1.0", optional = true }
|
||||
derivative = { version = "2.0", features = ["use_core"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
default = ["std", "r1cs"]
|
||||
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"]
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::{Namespace, SynthesisError};
|
||||
|
||||
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 r1cs_std::prelude::*;
|
||||
@@ -11,126 +12,58 @@ use r1cs_std::prelude::*;
|
||||
use core::borrow::Borrow;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Blake2sParametersGadget;
|
||||
pub struct ParametersVar;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Blake2sRandomnessGadget(pub Vec<UInt8>);
|
||||
pub struct RandomnessVar<F: Field>(pub Vec<UInt8<F>>);
|
||||
|
||||
pub struct Blake2sCommitmentGadget;
|
||||
pub struct CommGadget;
|
||||
|
||||
impl<ConstraintF: PrimeField> CommitmentGadget<Blake2sCommitment, ConstraintF>
|
||||
for Blake2sCommitmentGadget
|
||||
{
|
||||
type OutputGadget = Blake2sOutputGadget;
|
||||
type ParametersGadget = Blake2sParametersGadget;
|
||||
type RandomnessGadget = Blake2sRandomnessGadget;
|
||||
impl<F: PrimeField> CommitmentGadget<blake2s::Commitment, F> for CommGadget {
|
||||
type OutputVar = OutputVar<F>;
|
||||
type ParametersVar = ParametersVar;
|
||||
type RandomnessVar = RandomnessVar<F>;
|
||||
|
||||
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
_: &Self::ParametersGadget,
|
||||
input: &[UInt8],
|
||||
r: &Self::RandomnessGadget,
|
||||
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
fn commit(
|
||||
_: &Self::ParametersVar,
|
||||
input: &[UInt8<F>],
|
||||
r: &Self::RandomnessVar,
|
||||
) -> Result<Self::OutputVar, SynthesisError> {
|
||||
let mut input_bits = Vec::with_capacity(512);
|
||||
for byte in input.iter().chain(r.0.iter()) {
|
||||
input_bits.extend_from_slice(&byte.into_bits_le());
|
||||
}
|
||||
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);
|
||||
}
|
||||
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<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sRand
|
||||
mod test {
|
||||
use crate::{
|
||||
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 r1cs_core::ConstraintSystem;
|
||||
use r1cs_std::{prelude::*, test_constraint_system::TestConstraintSystem};
|
||||
use r1cs_std::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
#[test]
|
||||
fn commitment_gadget_test() {
|
||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
|
||||
let input = [1u8; 32];
|
||||
|
||||
let rng = &mut test_rng();
|
||||
|
||||
type TestCOMM = Blake2sCommitment;
|
||||
type TestCOMMGadget = Blake2sCommitmentGadget;
|
||||
type TestCOMM = Commitment;
|
||||
type TestCOMMGadget = CommGadget;
|
||||
|
||||
let mut randomness = [0u8; 32];
|
||||
rng.fill(&mut randomness);
|
||||
|
||||
let parameters = ();
|
||||
let primitive_result = Blake2sCommitment::commit(¶meters, &input, &randomness).unwrap();
|
||||
let primitive_result = Commitment::commit(¶meters, &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(¶meters),
|
||||
)
|
||||
.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(
|
||||
¶meters_var,
|
||||
&input_var,
|
||||
&randomness_var,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
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,12 +4,12 @@ use blake2::Blake2s as b2s;
|
||||
use digest::Digest;
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Blake2sCommitment;
|
||||
pub struct Commitment;
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
pub mod constraints;
|
||||
|
||||
impl CommitmentScheme for Blake2sCommitment {
|
||||
impl CommitmentScheme for Commitment {
|
||||
type Parameters = ();
|
||||
type Randomness = [u8; 32];
|
||||
type Output = [u8; 32];
|
||||
@@ -21,11 +21,11 @@ impl CommitmentScheme for Blake2sCommitment {
|
||||
fn commit(
|
||||
_: &Self::Parameters,
|
||||
input: &[u8],
|
||||
randomness: &Self::Randomness,
|
||||
r: &Self::Randomness,
|
||||
) -> Result<Self::Output, Error> {
|
||||
let mut h = b2s::new();
|
||||
h.input(input);
|
||||
h.input(randomness.as_ref());
|
||||
h.input(r.as_ref());
|
||||
let mut result = [0u8; 32];
|
||||
result.copy_from_slice(&h.result());
|
||||
Ok(result)
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
use crate::CommitmentScheme;
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use algebra_core::Field;
|
||||
use core::fmt::Debug;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::SynthesisError;
|
||||
use r1cs_std::prelude::*;
|
||||
|
||||
pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> {
|
||||
type OutputGadget: EqGadget<ConstraintF>
|
||||
type OutputVar: EqGadget<ConstraintF>
|
||||
+ ToBytesGadget<ConstraintF>
|
||||
+ AllocGadget<C::Output, ConstraintF>
|
||||
+ AllocVar<C::Output, ConstraintF>
|
||||
+ R1CSVar<ConstraintF>
|
||||
+ Clone
|
||||
+ Sized
|
||||
+ 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>;
|
||||
}
|
||||
|
||||
@@ -1,62 +1,59 @@
|
||||
use algebra_core::{Field, PrimeField};
|
||||
|
||||
use crate::commitment::{
|
||||
injective_map::{InjectiveMap, PedersenCommCompressor},
|
||||
pedersen::{
|
||||
constraints::{
|
||||
PedersenCommitmentGadget, PedersenCommitmentGadgetParameters, PedersenRandomnessGadget,
|
||||
},
|
||||
PedersenWindow,
|
||||
constraints::{CommGadget, ParametersVar, RandomnessVar},
|
||||
Window,
|
||||
},
|
||||
CommitmentGadget,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
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_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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,29 @@ use crate::Error;
|
||||
use core::marker::PhantomData;
|
||||
use rand::Rng;
|
||||
|
||||
use super::{
|
||||
pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness, PedersenWindow},
|
||||
CommitmentScheme,
|
||||
};
|
||||
use super::{pedersen, CommitmentScheme};
|
||||
pub use crate::crh::injective_map::InjectiveMap;
|
||||
use algebra_core::groups::Group;
|
||||
use algebra_core::ProjectiveCurve;
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
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>,
|
||||
_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 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> {
|
||||
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);
|
||||
params
|
||||
}
|
||||
@@ -38,7 +35,7 @@ impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> CommitmentScheme
|
||||
randomness: &Self::Randomness,
|
||||
) -> Result<Self::Output, Error> {
|
||||
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,
|
||||
)?)?;
|
||||
end_timer!(eval_time);
|
||||
|
||||
@@ -1,57 +1,63 @@
|
||||
use crate::{
|
||||
commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness},
|
||||
crh::pedersen::PedersenWindow,
|
||||
commitment::pedersen::{Commitment, Parameters, Randomness},
|
||||
crh::pedersen::Window,
|
||||
Vec,
|
||||
};
|
||||
use algebra_core::{
|
||||
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 r1cs_std::prelude::*;
|
||||
|
||||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))]
|
||||
pub struct PedersenCommitmentGadgetParameters<G: Group, W: PedersenWindow, ConstraintF: Field> {
|
||||
params: PedersenParameters<G>,
|
||||
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
|
||||
pub struct ParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
|
||||
where
|
||||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
||||
{
|
||||
params: Parameters<C>,
|
||||
#[doc(hidden)]
|
||||
_group: PhantomData<G>,
|
||||
#[doc(hidden)]
|
||||
_engine: PhantomData<ConstraintF>,
|
||||
#[doc(hidden)]
|
||||
_window: PhantomData<W>,
|
||||
_group_var: PhantomData<GG>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PedersenRandomnessGadget(Vec<UInt8>);
|
||||
pub struct RandomnessVar<F: Field>(Vec<UInt8<F>>);
|
||||
|
||||
pub struct PedersenCommitmentGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>(
|
||||
#[doc(hidden)] PhantomData<*const G>,
|
||||
#[doc(hidden)] PhantomData<*const GG>,
|
||||
PhantomData<ConstraintF>,
|
||||
);
|
||||
|
||||
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
|
||||
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));
|
||||
|
||||
let mut padded_input = input.to_vec();
|
||||
@@ -72,16 +78,12 @@ where
|
||||
.flat_map(|byte| byte.into_bits_le())
|
||||
.collect();
|
||||
let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
|
||||
let mut result = GG::precomputed_base_multiscalar_mul(
|
||||
cs.ns(|| "multiexp"),
|
||||
¶meters.params.generators,
|
||||
input_in_bits,
|
||||
)?;
|
||||
let mut result =
|
||||
GG::precomputed_base_multiscalar_mul(¶meters.params.generators, input_in_bits)?;
|
||||
|
||||
// Compute h^r
|
||||
let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect();
|
||||
result.precomputed_base_scalar_mul(
|
||||
cs.ns(|| "Randomizer"),
|
||||
rand_bits
|
||||
.iter()
|
||||
.zip(¶meters.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
|
||||
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
|
||||
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 {
|
||||
use algebra::{
|
||||
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr},
|
||||
test_rng, ProjectiveCurve, UniformRand,
|
||||
test_rng, UniformRand,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
commitment::{
|
||||
pedersen::{
|
||||
constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness,
|
||||
},
|
||||
pedersen::{constraints::CommGadget, Commitment, Randomness},
|
||||
CommitmentGadget, CommitmentScheme,
|
||||
},
|
||||
crh::pedersen::PedersenWindow,
|
||||
crh::pedersen,
|
||||
};
|
||||
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]
|
||||
fn commitment_gadget_test() {
|
||||
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Window;
|
||||
|
||||
impl PedersenWindow for Window {
|
||||
impl pedersen::Window for Window {
|
||||
const WINDOW_SIZE: usize = 4;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
@@ -234,45 +164,37 @@ mod test {
|
||||
|
||||
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 =
|
||||
PedersenCommitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
|
||||
Commitment::<JubJub, Window>::commit(¶meters, &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),
|
||||
)
|
||||
.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(¶meters),
|
||||
)
|
||||
.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(¶meters_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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::{Error, Vec};
|
||||
use algebra_core::{
|
||||
bytes::ToBytes,
|
||||
groups::Group,
|
||||
io::{Result as IoResult, Write},
|
||||
BitIterator, Field, FpParameters, PrimeField, ToConstraintField, UniformRand,
|
||||
BitIterator, Field, FpParameters, PrimeField, ProjectiveCurve, ToConstraintField, UniformRand,
|
||||
};
|
||||
|
||||
use core::marker::PhantomData;
|
||||
@@ -11,64 +10,55 @@ use rand::Rng;
|
||||
|
||||
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")]
|
||||
pub mod constraints;
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[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);
|
||||
#[derivative(Clone, PartialEq, Debug, Eq, Default)]
|
||||
pub struct Randomness<C: ProjectiveCurve>(pub C::ScalarField);
|
||||
|
||||
impl<G: Group> UniformRand for PedersenRandomness<G> {
|
||||
impl<C: ProjectiveCurve> UniformRand for Randomness<C> {
|
||||
#[inline]
|
||||
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<()> {
|
||||
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> {
|
||||
let time = start_timer!(|| format!(
|
||||
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
|
||||
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C",
|
||||
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);
|
||||
|
||||
Ok(Self::Parameters {
|
||||
@@ -102,10 +92,10 @@ impl<G: Group, W: PedersenWindow> CommitmentScheme for PedersenCommitment<G, W>
|
||||
|
||||
// Invoke Pedersen CRH here, to prevent code duplication.
|
||||
|
||||
let crh_parameters = PedersenCRHParameters {
|
||||
let crh_parameters = pedersen::Parameters {
|
||||
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");
|
||||
|
||||
// Compute h^r.
|
||||
@@ -122,12 +112,12 @@ impl<G: Group, W: PedersenWindow> CommitmentScheme for PedersenCommitment<G, W>
|
||||
end_timer!(randomize_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]
|
||||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
||||
|
||||
@@ -1,61 +1,60 @@
|
||||
use core::{borrow::Borrow, hash::Hash, marker::PhantomData};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::{
|
||||
crh::{
|
||||
bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE},
|
||||
pedersen::PedersenWindow,
|
||||
bowe_hopwood::{Parameters, CHUNK_SIZE, CRH},
|
||||
pedersen::Window,
|
||||
FixedLengthCRHGadget,
|
||||
},
|
||||
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;
|
||||
|
||||
type ConstraintF<P> = <<P as ModelParameters>::BaseField as Field>::BasePrimeField;
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
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
|
||||
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>;
|
||||
type OutputVar = AffineVar<P, F>;
|
||||
type ParametersVar = ParametersVar<P, W>;
|
||||
|
||||
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
cs: CS,
|
||||
parameters: &Self::ParametersGadget,
|
||||
input: &[UInt8],
|
||||
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
fn evaluate(
|
||||
parameters: &Self::ParametersVar,
|
||||
input: &[UInt8<ConstraintF<P>>],
|
||||
) -> Result<Self::OutputVar, SynthesisError> {
|
||||
// Pad the input if it is not the current length.
|
||||
let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect();
|
||||
if (input_in_bits.len()) % CHUNK_SIZE != 0 {
|
||||
@@ -75,8 +74,7 @@ where
|
||||
.chunks(W::WINDOW_SIZE * CHUNK_SIZE)
|
||||
.map(|x| x.chunks(CHUNK_SIZE).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(
|
||||
¶meters.params.generators,
|
||||
&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,
|
||||
_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,
|
||||
})
|
||||
}
|
||||
@@ -140,22 +106,19 @@ mod test {
|
||||
use rand::Rng;
|
||||
|
||||
use crate::crh::{
|
||||
bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH},
|
||||
pedersen::PedersenWindow,
|
||||
bowe_hopwood::{constraints::CRHGadget, CRH},
|
||||
pedersen::Window as PedersenWindow,
|
||||
FixedLengthCRH, FixedLengthCRHGadget,
|
||||
};
|
||||
use algebra::{
|
||||
ed_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr},
|
||||
ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
|
||||
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)]
|
||||
pub(super) struct Window;
|
||||
@@ -165,35 +128,34 @@ mod test {
|
||||
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,
|
||||
) -> ([u8; 189], Vec<UInt8>) {
|
||||
) -> ([u8; 189], Vec<UInt8<Fr>>) {
|
||||
let mut input = [1u8; 189];
|
||||
rng.fill_bytes(&mut input);
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crh_primitive_gadget_test() {
|
||||
fn test_native_equality() {
|
||||
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());
|
||||
|
||||
let parameters = TestCRH::setup(rng).unwrap();
|
||||
let primitive_result = TestCRH::evaluate(¶meters, &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(¶meters),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -202,19 +164,12 @@ mod test {
|
||||
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(¶meters_var, &input_var).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());
|
||||
assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@ use rand::Rng;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
use super::pedersen::{bytes_to_bits, PedersenCRH, PedersenWindow};
|
||||
use super::pedersen;
|
||||
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;
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
@@ -17,22 +20,23 @@ pub mod constraints;
|
||||
|
||||
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>,
|
||||
}
|
||||
|
||||
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();
|
||||
for _ in 0..W::NUM_WINDOWS {
|
||||
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 {
|
||||
generators_for_segment.push(base);
|
||||
for _ in 0..4 {
|
||||
@@ -45,10 +49,10 @@ impl<G: Group, W: PedersenWindow> BoweHopwoodPedersenCRH<G, W> {
|
||||
}
|
||||
}
|
||||
|
||||
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 calculate_num_chunks_in_segment<F: PrimeField>() -> usize {
|
||||
@@ -63,10 +67,10 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W
|
||||
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 {
|
||||
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_num_chunks_in_segment
|
||||
)
|
||||
@@ -74,7 +78,7 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W
|
||||
}
|
||||
|
||||
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::WINDOW_SIZE,
|
||||
W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE
|
||||
@@ -98,7 +102,7 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W
|
||||
}
|
||||
|
||||
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.
|
||||
padded_input.extend_from_slice(&input);
|
||||
if input.len() % CHUNK_SIZE != 0 {
|
||||
@@ -143,13 +147,13 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W
|
||||
encoded += &generator.double();
|
||||
}
|
||||
if chunk_bits[2] {
|
||||
encoded = encoded.neg();
|
||||
encoded = -encoded;
|
||||
}
|
||||
encoded
|
||||
})
|
||||
.sum::<G>()
|
||||
.sum::<TEProjective<P>>()
|
||||
})
|
||||
.sum::<G>();
|
||||
.sum::<TEProjective<P>>();
|
||||
|
||||
end_timer!(eval_time);
|
||||
|
||||
@@ -157,9 +161,9 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> Debug for BoweHopwoodPedersenParameters<G> {
|
||||
impl<P: TEModelParameters> Debug for Parameters<P> {
|
||||
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() {
|
||||
write!(f, "\t Generator {}: {:?}\n", i, g)?;
|
||||
}
|
||||
@@ -170,28 +174,23 @@ impl<G: Group> Debug for BoweHopwoodPedersenParameters<G> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
crh::{bowe_hopwood::BoweHopwoodPedersenCRH, pedersen::PedersenWindow},
|
||||
crh::{bowe_hopwood::CRH, pedersen::Window},
|
||||
FixedLengthCRH,
|
||||
};
|
||||
use algebra::{ed_on_bls12_381::EdwardsProjective, test_rng};
|
||||
use algebra::{ed_on_bls12_381::EdwardsParameters, test_rng};
|
||||
|
||||
#[test]
|
||||
fn test_simple_bh() {
|
||||
#[derive(Clone)]
|
||||
struct TestWindow {}
|
||||
impl PedersenWindow for TestWindow {
|
||||
impl Window for TestWindow {
|
||||
const WINDOW_SIZE: usize = 63;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
|
||||
let rng = &mut test_rng();
|
||||
let params =
|
||||
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::setup(rng)
|
||||
.unwrap();
|
||||
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::evaluate(
|
||||
¶ms,
|
||||
&[1, 2, 3],
|
||||
)
|
||||
.unwrap();
|
||||
let params = <CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::setup(rng).unwrap();
|
||||
<CRH<EdwardsParameters, TestWindow> as FixedLengthCRH>::evaluate(¶ms, &[1, 2, 3])
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,24 @@ use algebra_core::Field;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crate::crh::FixedLengthCRH;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::SynthesisError;
|
||||
|
||||
use r1cs_std::prelude::*;
|
||||
|
||||
pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized {
|
||||
type OutputGadget: ConditionalEqGadget<ConstraintF>
|
||||
+ EqGadget<ConstraintF>
|
||||
type OutputVar: EqGadget<ConstraintF>
|
||||
+ ToBytesGadget<ConstraintF>
|
||||
+ CondSelectGadget<ConstraintF>
|
||||
+ AllocGadget<H::Output, ConstraintF>
|
||||
+ AllocVar<H::Output, ConstraintF>
|
||||
+ R1CSVar<ConstraintF>
|
||||
+ Debug
|
||||
+ Clone
|
||||
+ 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>;
|
||||
}
|
||||
|
||||
@@ -2,129 +2,96 @@ use core::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use crate::crh::{
|
||||
injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor},
|
||||
pedersen::{
|
||||
constraints::{PedersenCRHGadget, PedersenCRHGadgetParameters},
|
||||
PedersenWindow,
|
||||
},
|
||||
pedersen::{constraints as ped_constraints, Window},
|
||||
FixedLengthCRHGadget,
|
||||
};
|
||||
|
||||
use algebra_core::{
|
||||
curves::{
|
||||
models::{ModelParameters, TEModelParameters},
|
||||
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
|
||||
twisted_edwards_extended::GroupProjective as TEProjective,
|
||||
},
|
||||
fields::{Field, PrimeField, SquareRootField},
|
||||
groups::Group,
|
||||
ProjectiveCurve,
|
||||
};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::SynthesisError;
|
||||
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::*,
|
||||
};
|
||||
|
||||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
||||
|
||||
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
|
||||
+ Clone
|
||||
+ 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;
|
||||
|
||||
impl<ConstraintF, P>
|
||||
InjectiveMapGadget<
|
||||
TEAffine<P>,
|
||||
TECompressor,
|
||||
ConstraintF,
|
||||
TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
|
||||
> for TECompressorGadget
|
||||
impl<F, P> InjectiveMapGadget<TEProjective<P>, TECompressor, TEVar<P, FpVar<F>>>
|
||||
for TECompressorGadget
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<ConstraintF, P>
|
||||
InjectiveMapGadget<
|
||||
TEProjective<P>,
|
||||
TECompressor,
|
||||
ConstraintF,
|
||||
TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
|
||||
> for TECompressorGadget
|
||||
pub struct PedersenCRHCompressorGadget<C, I, W, GG, IG>
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PedersenCRHCompressorGadget<G, I, ConstraintF, GG, IG>
|
||||
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>,
|
||||
#[doc(hidden)]
|
||||
_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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,30 +3,25 @@ use algebra_core::bytes::ToBytes;
|
||||
use core::{fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
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")]
|
||||
pub mod constraints;
|
||||
|
||||
pub trait InjectiveMap<G: Group> {
|
||||
pub trait InjectiveMap<C: ProjectiveCurve> {
|
||||
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;
|
||||
|
||||
impl<P: TEModelParameters> InjectiveMap<TEAffine<P>> for TECompressor {
|
||||
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
|
||||
type Output = <P as ModelParameters>::BaseField;
|
||||
|
||||
fn injective_map(ge: &TEAffine<P>) -> Result<Self::Output, CryptoError> {
|
||||
@@ -35,39 +30,29 @@ impl<P: TEModelParameters> InjectiveMap<TEAffine<P>> for TECompressor {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
|
||||
type Output = <P as ModelParameters>::BaseField;
|
||||
|
||||
fn injective_map(ge: &TEProjective<P>) -> Result<Self::Output, CryptoError> {
|
||||
let ge = ge.into_affine();
|
||||
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
|
||||
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>,
|
||||
_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 Parameters = PedersenParameters<G>;
|
||||
type Parameters = pedersen::Parameters<C>;
|
||||
|
||||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
||||
let time = start_timer!(|| format!("PedersenCRHCompressor::Setup"));
|
||||
let params = PedersenCRH::<G, W>::setup(rng);
|
||||
let params = pedersen::CRH::<C, W>::setup(rng);
|
||||
end_timer!(time);
|
||||
params
|
||||
}
|
||||
|
||||
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
|
||||
let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval");
|
||||
let result = I::injective_map(&PedersenCRH::<G, W>::evaluate(parameters, input)?)?;
|
||||
let result = I::injective_map(&pedersen::CRH::<C, W>::evaluate(parameters, input)?)?;
|
||||
end_timer!(eval_time);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ pub use constraints::*;
|
||||
|
||||
pub trait FixedLengthCRH {
|
||||
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;
|
||||
|
||||
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
|
||||
|
||||
@@ -1,57 +1,54 @@
|
||||
use crate::{
|
||||
crh::{
|
||||
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
|
||||
pedersen::{Parameters, Window, CRH},
|
||||
FixedLengthCRHGadget,
|
||||
},
|
||||
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 core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
#[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>,
|
||||
_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>,
|
||||
}
|
||||
|
||||
impl<ConstraintF, G, GG, W> FixedLengthCRHGadget<PedersenCRH<G, W>, ConstraintF>
|
||||
for PedersenCRHGadget<G, ConstraintF, GG>
|
||||
#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
|
||||
pub struct CRHParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
|
||||
where
|
||||
ConstraintF: Field,
|
||||
G: Group,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
W: PedersenWindow,
|
||||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
||||
{
|
||||
type OutputGadget = GG;
|
||||
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
|
||||
params: Parameters<C>,
|
||||
#[doc(hidden)]
|
||||
_group_g: PhantomData<GG>,
|
||||
}
|
||||
|
||||
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
cs: CS,
|
||||
parameters: &Self::ParametersGadget,
|
||||
input: &[UInt8],
|
||||
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
|
||||
pub struct CRHGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>, W: Window>
|
||||
where
|
||||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
||||
{
|
||||
#[doc(hidden)]
|
||||
_group: PhantomData<*const C>,
|
||||
#[doc(hidden)]
|
||||
_group_var: PhantomData<*const GG>,
|
||||
#[doc(hidden)]
|
||||
_window: PhantomData<*const W>,
|
||||
}
|
||||
|
||||
impl<C, GG, W> FixedLengthCRHGadget<CRH<C, W>, ConstraintF<C>> for CRHGadget<C, GG, W>
|
||||
where
|
||||
C: ProjectiveCurve,
|
||||
GG: CurveVar<C, ConstraintF<C>>,
|
||||
W: Window,
|
||||
for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
|
||||
{
|
||||
type OutputVar = GG;
|
||||
type ParametersVar = CRHParametersVar<C, GG>;
|
||||
|
||||
fn evaluate(
|
||||
parameters: &Self::ParametersVar,
|
||||
input: &[UInt8<ConstraintF<C>>],
|
||||
) -> Result<Self::OutputVar, SynthesisError> {
|
||||
let mut padded_input = input.to_vec();
|
||||
// Pad the input if it is not the current length.
|
||||
if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
@@ -64,145 +61,87 @@ where
|
||||
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
|
||||
|
||||
// Allocate new variable for the result.
|
||||
let input_in_bits: Vec<_> = padded_input
|
||||
.iter()
|
||||
.flat_map(|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 result =
|
||||
GG::precomputed_base_multiscalar_mul(cs, ¶meters.params.generators, input_in_bits)?;
|
||||
GG::precomputed_base_multiscalar_mul(¶meters.params.generators, input_in_bits)?;
|
||||
|
||||
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,
|
||||
_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)]
|
||||
mod test {
|
||||
use crate::crh::{
|
||||
pedersen::{constraints::PedersenCRHGadget, PedersenCRH, PedersenWindow},
|
||||
FixedLengthCRH, FixedLengthCRHGadget,
|
||||
};
|
||||
use crate::crh::{pedersen, pedersen::constraints::*, FixedLengthCRH, FixedLengthCRHGadget};
|
||||
use algebra::{
|
||||
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;
|
||||
|
||||
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)]
|
||||
pub(super) struct Window;
|
||||
|
||||
impl PedersenWindow for Window {
|
||||
impl pedersen::Window for Window {
|
||||
const WINDOW_SIZE: usize = 128;
|
||||
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,
|
||||
) -> ([u8; 128], Vec<UInt8>) {
|
||||
) -> ([u8; 128], Vec<UInt8<Fr>>) {
|
||||
let mut input = [1u8; 128];
|
||||
rng.fill_bytes(&mut input);
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crh_primitive_gadget_test() {
|
||||
fn test_native_equality() {
|
||||
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 primitive_result = TestCRH::evaluate(¶meters, &input).unwrap();
|
||||
|
||||
let gadget_parameters =
|
||||
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
|
||||
&mut cs.ns(|| "gadget_parameters"),
|
||||
|| Ok(¶meters),
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
"number of constraints for input + params: {}",
|
||||
cs.num_constraints()
|
||||
);
|
||||
let parameters_var =
|
||||
CRHParametersVar::new_constant(cs.ns("CRH Parameters"), ¶meters).unwrap();
|
||||
|
||||
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(¶meters_var, &input_var).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 primitive_result = primitive_result;
|
||||
assert_eq!(primitive_result, result_var.value().unwrap());
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,29 +8,29 @@ use rand::Rng;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::crh::FixedLengthCRH;
|
||||
use algebra_core::{groups::Group, Field, ToConstraintField};
|
||||
use algebra_core::{Field, ProjectiveCurve, ToConstraintField};
|
||||
use ff_fft::cfg_chunks;
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
pub mod constraints;
|
||||
|
||||
pub trait PedersenWindow: Clone {
|
||||
pub trait Window: Clone {
|
||||
const WINDOW_SIZE: usize;
|
||||
const NUM_WINDOWS: usize;
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
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();
|
||||
for _ in 0..W::NUM_WINDOWS {
|
||||
generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng));
|
||||
@@ -38,9 +38,9 @@ impl<G: Group, W: PedersenWindow> PedersenCRH<G, W> {
|
||||
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 base = G::rand(rng);
|
||||
let mut base = C::rand(rng);
|
||||
for _ in 0..num_powers {
|
||||
cur_gen_powers.push(base);
|
||||
base.double_in_place();
|
||||
@@ -49,14 +49,14 @@ impl<G: Group, W: PedersenWindow> PedersenCRH<G, W> {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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> {
|
||||
let time = start_timer!(|| format!(
|
||||
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
|
||||
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> C",
|
||||
W::NUM_WINDOWS,
|
||||
W::WINDOW_SIZE,
|
||||
W::NUM_WINDOWS * W::WINDOW_SIZE
|
||||
@@ -71,7 +71,7 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
|
||||
|
||||
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
panic!(
|
||||
"incorrect input length {:?} for window params {:?}x{:?}",
|
||||
"incorrect input length {:?} for window params {:?}✕{:?}",
|
||||
input.len(),
|
||||
W::WINDOW_SIZE,
|
||||
W::NUM_WINDOWS
|
||||
@@ -93,7 +93,7 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
|
||||
assert_eq!(
|
||||
parameters.generators.len(),
|
||||
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.len(),
|
||||
W::WINDOW_SIZE,
|
||||
@@ -105,7 +105,7 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
|
||||
let result = cfg_chunks!(bits, W::WINDOW_SIZE)
|
||||
.zip(¶meters.generators)
|
||||
.map(|(bits, generator_powers)| {
|
||||
let mut encoded = G::zero();
|
||||
let mut encoded = C::zero();
|
||||
for (bit, base) in bits.iter().zip(generator_powers.iter()) {
|
||||
if *bit {
|
||||
encoded += base;
|
||||
@@ -113,11 +113,11 @@ impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
|
||||
}
|
||||
encoded
|
||||
})
|
||||
.sum::<G>();
|
||||
.sum::<C>();
|
||||
|
||||
end_timer!(eval_time);
|
||||
|
||||
Ok(result)
|
||||
Ok(result.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
bits
|
||||
}
|
||||
|
||||
impl<G: Group> Debug for PedersenParameters<G> {
|
||||
impl<C: ProjectiveCurve> Debug for Parameters<C> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "Pedersen Hash Parameters {{\n")?;
|
||||
for (i, g) in self.generators.iter().enumerate() {
|
||||
@@ -142,8 +142,8 @@ impl<G: Group> Debug for PedersenParameters<G> {
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
||||
|
||||
@@ -25,17 +25,17 @@ pub mod signature;
|
||||
pub use self::{
|
||||
commitment::CommitmentScheme,
|
||||
crh::FixedLengthCRH,
|
||||
merkle_tree::{MerkleHashTree, MerkleTreePath},
|
||||
merkle_tree::{MerkleTree, Path},
|
||||
nizk::NIZK,
|
||||
prf::PRF,
|
||||
signature::SignatureScheme,
|
||||
};
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
#[cfg(feature = "r1cs")]
|
||||
pub use self::{
|
||||
commitment::CommitmentGadget, crh::FixedLengthCRHGadget,
|
||||
merkle_tree::constraints::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>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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::{
|
||||
crh::{FixedLengthCRH, FixedLengthCRHGadget},
|
||||
@@ -9,58 +9,56 @@ use crate::{
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
pub struct MerkleTreePathGadget<P, HGadget, ConstraintF>
|
||||
pub struct PathVar<P, HGadget, ConstraintF>
|
||||
where
|
||||
P: MerkleTreeConfig,
|
||||
P: Config,
|
||||
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
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
|
||||
P: MerkleTreeConfig,
|
||||
P: Config,
|
||||
ConstraintF: Field,
|
||||
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
<CRHGadget::OutputVar as R1CSVar<ConstraintF>>::Value: PartialEq,
|
||||
{
|
||||
pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
|
||||
pub fn check_membership(
|
||||
&self,
|
||||
cs: CS,
|
||||
parameters: &CRHGadget::ParametersGadget,
|
||||
root: &CRHGadget::OutputGadget,
|
||||
parameters: &CRHGadget::ParametersVar,
|
||||
root: &CRHGadget::OutputVar,
|
||||
leaf: impl ToBytesGadget<ConstraintF>,
|
||||
) -> 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,
|
||||
mut cs: CS,
|
||||
parameters: &CRHGadget::ParametersGadget,
|
||||
root: &CRHGadget::OutputGadget,
|
||||
parameters: &CRHGadget::ParametersVar,
|
||||
root: &CRHGadget::OutputVar,
|
||||
leaf: impl ToBytesGadget<ConstraintF>,
|
||||
should_enforce: &Boolean,
|
||||
should_enforce: &Boolean<ConstraintF>,
|
||||
) -> Result<(), SynthesisError> {
|
||||
assert_eq!(self.path.len(), P::HEIGHT - 1);
|
||||
// Check that the hash of the given leaf matches the leaf hash in the membership
|
||||
// proof.
|
||||
let leaf_bits = leaf.to_bytes(&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.
|
||||
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_hash,
|
||||
&self.path[0].0,
|
||||
&self.path[0].1,
|
||||
should_enforce,
|
||||
@@ -68,133 +66,74 @@ where
|
||||
|
||||
// Check levels between leaf level and root.
|
||||
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.
|
||||
let previous_is_left =
|
||||
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
|
||||
Ok(&previous_hash == left_hash)
|
||||
})?
|
||||
.into();
|
||||
let previous_is_left = Boolean::new_witness(cs.ns("previous_is_left"), || {
|
||||
Ok(previous_hash.value()?.eq(&left_hash.value()?))
|
||||
})?;
|
||||
|
||||
CRHGadget::OutputGadget::conditional_enforce_equal_or(
|
||||
&mut cs.ns(|| format!("check_equals_which_{}", i)),
|
||||
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_hash,
|
||||
left_hash,
|
||||
right_hash,
|
||||
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
|
||||
ConstraintF: Field,
|
||||
CS: ConstraintSystem<ConstraintF>,
|
||||
H: FixedLengthCRH,
|
||||
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;
|
||||
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
|
||||
P: MerkleTreeConfig,
|
||||
P: Config,
|
||||
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
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 {
|
||||
use crate::{
|
||||
crh::{
|
||||
pedersen::{constraints::PedersenCRHGadget, PedersenCRH, PedersenWindow},
|
||||
pedersen::{self, constraints::CRHGadget},
|
||||
FixedLengthCRH, FixedLengthCRHGadget,
|
||||
},
|
||||
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 rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use super::*;
|
||||
use r1cs_std::{ed_on_bls12_381::EdwardsGadget, test_constraint_system::TestConstraintSystem};
|
||||
use r1cs_std::ed_on_bls12_381::EdwardsVar;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Window4x256;
|
||||
impl PedersenWindow for Window4x256 {
|
||||
impl pedersen::Window for Window4x256 {
|
||||
const WINDOW_SIZE: usize = 4;
|
||||
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;
|
||||
|
||||
impl MerkleTreeConfig for JubJubMerkleTreeParams {
|
||||
impl Config for JubJubMerkleTreeParams {
|
||||
const HEIGHT: usize = 32;
|
||||
type H = H;
|
||||
}
|
||||
|
||||
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
|
||||
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
|
||||
|
||||
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
|
||||
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
||||
@@ -242,13 +181,13 @@ mod test {
|
||||
let root = tree.root();
|
||||
let mut satisfied = true;
|
||||
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();
|
||||
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
||||
// 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 {
|
||||
Ok(<H as FixedLengthCRH>::Output::default())
|
||||
@@ -263,9 +202,9 @@ mod test {
|
||||
println!("constraints from digest: {}", constraints_from_digest);
|
||||
|
||||
// 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();
|
||||
|
||||
@@ -283,26 +222,21 @@ mod test {
|
||||
println!("constraints from leaf: {}", constraints_from_leaf);
|
||||
|
||||
// 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()
|
||||
- constraints_from_parameters
|
||||
- constraints_from_digest
|
||||
- constraints_from_leaf;
|
||||
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;
|
||||
println!(
|
||||
"Unsatisfied constraint: {}",
|
||||
|
||||
@@ -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;
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
pub mod constraints;
|
||||
|
||||
pub trait MerkleTreeConfig {
|
||||
pub trait Config {
|
||||
const HEIGHT: usize;
|
||||
type H: FixedLengthCRH;
|
||||
}
|
||||
@@ -14,20 +14,20 @@ pub trait MerkleTreeConfig {
|
||||
/// Our path `is_left_child()` if the boolean in `path` is true.
|
||||
#[derive(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<(
|
||||
<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 {
|
||||
let mut path = Vec::with_capacity(P::HEIGHT as usize);
|
||||
for _i in 1..P::HEIGHT as usize {
|
||||
@@ -40,13 +40,13 @@ impl<P: MerkleTreeConfig> Default for MerkleTreePath<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: MerkleTreeConfig> MerkleTreePath<P> {
|
||||
impl<P: Config> Path<P> {
|
||||
pub fn verify<L: ToBytes>(
|
||||
&self,
|
||||
parameters: &<P::H as FixedLengthCRH>::Parameters,
|
||||
root_hash: &<P::H as FixedLengthCRH>::Output,
|
||||
leaf: &L,
|
||||
) -> Result<bool, Error> {
|
||||
) -> Result<bool, crate::Error> {
|
||||
if self.path.len() != (P::HEIGHT - 1) as usize {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl<P: MerkleTreeConfig> MerkleTreePath<P> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MerkleHashTree<P: MerkleTreeConfig> {
|
||||
pub struct MerkleTree<P: Config> {
|
||||
tree: Vec<<P::H as FixedLengthCRH>::Output>,
|
||||
padding_tree: Vec<(
|
||||
<P::H as FixedLengthCRH>::Output,
|
||||
@@ -91,11 +91,11 @@ pub struct MerkleHashTree<P: MerkleTreeConfig> {
|
||||
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 fn blank(parameters: <P::H as FixedLengthCRH>::Parameters) -> Self {
|
||||
MerkleHashTree {
|
||||
MerkleTree {
|
||||
tree: Vec::new(),
|
||||
padding_tree: Vec::new(),
|
||||
root: None,
|
||||
@@ -106,7 +106,7 @@ impl<P: MerkleTreeConfig> MerkleHashTree<P> {
|
||||
pub fn new<L: ToBytes>(
|
||||
parameters: <P::H as FixedLengthCRH>::Parameters,
|
||||
leaves: &[L],
|
||||
) -> Result<Self, Error> {
|
||||
) -> Result<Self, crate::Error> {
|
||||
let new_time = start_timer!(|| "MerkleTree::New");
|
||||
|
||||
let last_level_size = leaves.len().next_power_of_two();
|
||||
@@ -174,7 +174,7 @@ impl<P: MerkleTreeConfig> MerkleHashTree<P> {
|
||||
};
|
||||
end_timer!(new_time);
|
||||
|
||||
Ok(MerkleHashTree {
|
||||
Ok(MerkleTree {
|
||||
tree,
|
||||
padding_tree,
|
||||
parameters,
|
||||
@@ -191,7 +191,7 @@ impl<P: MerkleTreeConfig> MerkleHashTree<P> {
|
||||
&self,
|
||||
index: usize,
|
||||
leaf: &L,
|
||||
) -> Result<MerkleTreePath<P>, Error> {
|
||||
) -> Result<Path<P>, crate::Error> {
|
||||
let prove_time = start_timer!(|| "MerkleTree::GenProof");
|
||||
let mut path = Vec::new();
|
||||
|
||||
@@ -203,7 +203,7 @@ impl<P: MerkleTreeConfig> MerkleHashTree<P> {
|
||||
|
||||
// Check that the given index corresponds to the correct leaf.
|
||||
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.
|
||||
@@ -233,32 +233,30 @@ impl<P: MerkleTreeConfig> MerkleHashTree<P> {
|
||||
}
|
||||
end_timer!(prove_time);
|
||||
if path.len() != (Self::HEIGHT - 1) as usize {
|
||||
return Err(MerkleTreeError::IncorrectPathLength(path.len()).into());
|
||||
return Err(Error::IncorrectPathLength(path.len()).into());
|
||||
} else {
|
||||
Ok(MerkleTreePath { path })
|
||||
Ok(Path { path })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MerkleTreeError {
|
||||
pub enum Error {
|
||||
IncorrectLeafIndex(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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl algebra_core::Error for MerkleTreeError {}
|
||||
impl algebra_core::Error for Error {}
|
||||
|
||||
/// Returns the log2 value of the given number.
|
||||
#[inline]
|
||||
@@ -333,14 +331,11 @@ pub(crate) fn hash_inner_node<H: FixedLengthCRH>(
|
||||
left: &H::Output,
|
||||
right: &H::Output,
|
||||
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)])
|
||||
}
|
||||
|
||||
@@ -349,16 +344,17 @@ pub(crate) fn hash_leaf<H: FixedLengthCRH, L: ToBytes>(
|
||||
parameters: &H::Parameters,
|
||||
leaf: &L,
|
||||
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)])
|
||||
}
|
||||
|
||||
pub(crate) fn hash_empty<H: FixedLengthCRH>(
|
||||
parameters: &H::Parameters,
|
||||
) -> Result<H::Output, Error> {
|
||||
) -> Result<H::Output, crate::Error> {
|
||||
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
|
||||
H::evaluate(parameters, &empty_buffer)
|
||||
}
|
||||
@@ -366,29 +362,29 @@ pub(crate) fn hash_empty<H: FixedLengthCRH>(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
crh::{pedersen::*, *},
|
||||
crh::{pedersen, *},
|
||||
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_xorshift::XorShiftRng;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Window4x256;
|
||||
impl PedersenWindow for Window4x256 {
|
||||
impl pedersen::Window for Window4x256 {
|
||||
const WINDOW_SIZE: usize = 4;
|
||||
const NUM_WINDOWS: usize = 256;
|
||||
}
|
||||
|
||||
type H = PedersenCRH<JubJub, Window4x256>;
|
||||
type H = pedersen::CRH<JubJub, Window4x256>;
|
||||
|
||||
struct JubJubMerkleTreeParams;
|
||||
|
||||
impl MerkleTreeConfig for JubJubMerkleTreeParams {
|
||||
impl Config for JubJubMerkleTreeParams {
|
||||
const HEIGHT: usize = 8;
|
||||
type H = H;
|
||||
}
|
||||
type JubJubMerkleTree = MerkleHashTree<JubJubMerkleTreeParams>;
|
||||
type JubJubMerkleTree = MerkleTree<JubJubMerkleTreeParams>;
|
||||
|
||||
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
|
||||
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
||||
@@ -435,7 +431,7 @@ mod test {
|
||||
|
||||
let crh_parameters = H::setup(&mut rng).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() {
|
||||
let proof = tree.generate_proof(i, &leaf).unwrap();
|
||||
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
||||
@@ -1,47 +1,59 @@
|
||||
use algebra_core::Field;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use core::borrow::Borrow;
|
||||
use r1cs_core::{Namespace, SynthesisError};
|
||||
use r1cs_std::prelude::*;
|
||||
|
||||
use crate::nizk::NIZK;
|
||||
|
||||
pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> {
|
||||
type PreparedVerificationKeyGadget;
|
||||
type VerificationKeyGadget: AllocGadget<N::VerificationParameters, ConstraintF>
|
||||
type PreparedVerificationKeyVar;
|
||||
type VerificationKeyVar: AllocVar<N::VerificationParameters, 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>;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ mod test {
|
||||
bls12_377::{Bls12_377, Fr},
|
||||
One,
|
||||
};
|
||||
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::{lc, ConstraintSynthesizer, ConstraintSystemRef, SynthesisError, Variable};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct R1CSCircuit {
|
||||
@@ -83,20 +83,20 @@ mod test {
|
||||
}
|
||||
|
||||
impl ConstraintSynthesizer<Fr> for R1CSCircuit {
|
||||
fn generate_constraints<CS: ConstraintSystem<Fr>>(
|
||||
fn generate_constraints(
|
||||
self,
|
||||
cs: &mut CS,
|
||||
cs: ConstraintSystemRef<Fr>,
|
||||
) -> 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()))?;
|
||||
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(
|
||||
|| "check_one",
|
||||
|lc| lc + sum,
|
||||
|lc| lc + CS::one(),
|
||||
|lc| lc + input + witness,
|
||||
);
|
||||
cs.enforce_named_constraint(
|
||||
"enforce sum",
|
||||
lc!() + sum,
|
||||
lc!() + Variable::One,
|
||||
lc!() + input + witness,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use algebra_core::PrimeField;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError};
|
||||
|
||||
use crate::{prf::PRFGadget, Vec};
|
||||
use r1cs_std::prelude::*;
|
||||
@@ -76,30 +76,23 @@ const SIGMA: [[usize; 16]; 10] = [
|
||||
// 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,
|
||||
b: usize,
|
||||
c: usize,
|
||||
d: usize,
|
||||
x: &UInt32,
|
||||
y: &UInt32,
|
||||
x: &UInt32<ConstraintF>,
|
||||
y: &UInt32<ConstraintF>,
|
||||
) -> 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(())
|
||||
}
|
||||
@@ -107,7 +100,7 @@ fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
|
||||
// 3.2. Compression Function F
|
||||
// Compression function F takes as an argument the state vector "h",
|
||||
// message block vector "m" (last block is padded with zeros to full
|
||||
// block size, if required), 2w-bit_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
|
||||
// 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.
|
||||
@@ -151,10 +144,9 @@ fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
|
||||
// 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,
|
||||
f: bool,
|
||||
) -> Result<(), SynthesisError> {
|
||||
@@ -181,106 +173,29 @@ fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF
|
||||
|
||||
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 {
|
||||
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 {
|
||||
let mut cs = cs.ns(|| format!("round {}", i));
|
||||
|
||||
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 {
|
||||
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(())
|
||||
@@ -312,53 +227,32 @@ fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF
|
||||
// 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);
|
||||
let mut parameters = [0; 8];
|
||||
parameters[0] = 0x01010000 ^ 32;
|
||||
blake2s_gadget_with_parameters(cs, input, ¶meters)
|
||||
evaluate_blake2s_with_parameters(input, ¶meters)
|
||||
}
|
||||
|
||||
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],
|
||||
) -> Result<Vec<UInt32>, SynthesisError> {
|
||||
) -> Result<Vec<UInt32<F>>, SynthesisError> {
|
||||
assert!(input.len() % 8 == 0);
|
||||
|
||||
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]))?,
|
||||
);
|
||||
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>> = vec![];
|
||||
let mut blocks: Vec<Vec<UInt32<F>>> = vec![];
|
||||
|
||||
for block in input.chunks(512) {
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
@@ -404,134 +291,93 @@ use crate::prf::Blake2s;
|
||||
|
||||
pub struct Blake2sGadget;
|
||||
#[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,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
condition: &Boolean,
|
||||
should_enforce: &Boolean<ConstraintF>,
|
||||
) -> 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]
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
|
||||
impl<ConstraintF: PrimeField> PRFGadget<Blake2s, ConstraintF> for Blake2sGadget {
|
||||
type OutputGadget = Blake2sOutputGadget;
|
||||
impl<F: PrimeField> R1CSVar<F> for OutputVar<F> {
|
||||
type Value = [u8; 32];
|
||||
|
||||
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 cs(&self) -> Option<ConstraintSystemRef<F>> {
|
||||
self.0.cs()
|
||||
}
|
||||
|
||||
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
seed: &[UInt8],
|
||||
input: &[UInt8],
|
||||
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
fn value(&self) -> Result<Self::Value, SynthesisError> {
|
||||
let mut value = [0u8; 32];
|
||||
for (val_i, self_i) in value.iter_mut().zip(&self.0) {
|
||||
*val_i = self_i.value()?;
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> PRFGadget<Blake2s, F> for Blake2sGadget {
|
||||
type OutputVar = OutputVar<F>;
|
||||
|
||||
fn new_seed(cs: ConstraintSystemRef<F>, seed: &[u8; 32]) -> Vec<UInt8<F>> {
|
||||
UInt8::new_witness_vec(cs.ns("New Blake2s seed"), seed).unwrap()
|
||||
}
|
||||
|
||||
fn evaluate(seed: &[UInt8<F>], input: &[UInt8<F>]) -> Result<Self::OutputVar, SynthesisError> {
|
||||
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()
|
||||
.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_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 r1cs_core::ConstraintSystem;
|
||||
|
||||
use super::Blake2sGadget;
|
||||
use r1cs_std::{
|
||||
boolean::AllocatedBit, prelude::*, test_constraint_system::TestConstraintSystem,
|
||||
};
|
||||
use r1cs_std::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_blake2s_constraints() {
|
||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -571,7 +411,7 @@ mod test {
|
||||
use rand::Rng;
|
||||
|
||||
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];
|
||||
rng.fill(&mut seed);
|
||||
@@ -579,32 +419,25 @@ mod test {
|
||||
let mut input = [0u8; 32];
|
||||
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 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),
|
||||
)
|
||||
.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!(
|
||||
"which is unsatisfied: {:?}",
|
||||
cs.which_is_unsatisfied().unwrap()
|
||||
);
|
||||
}
|
||||
assert!(cs.is_satisfied());
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -612,27 +445,27 @@ mod test {
|
||||
// Test that 512 fixed leading bits (constants)
|
||||
// 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 input_bits: Vec<_> = (0..512)
|
||||
.map(|_| Boolean::constant(rng.gen()))
|
||||
.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();
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 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);
|
||||
}
|
||||
|
||||
@@ -651,25 +484,24 @@ mod test {
|
||||
let mut hash_result = Vec::with_capacity(h.output_size());
|
||||
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![];
|
||||
|
||||
for (byte_i, input_byte) in data.into_iter().enumerate() {
|
||||
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(
|
||||
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
|
||||
.iter()
|
||||
@@ -679,10 +511,10 @@ mod test {
|
||||
for b in chunk.to_bits_le() {
|
||||
match b {
|
||||
Boolean::Is(b) => {
|
||||
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||
assert!(s.next().unwrap() == b.value().unwrap());
|
||||
}
|
||||
Boolean::Not(b) => {
|
||||
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||
assert!(s.next().unwrap() != b.value().unwrap());
|
||||
}
|
||||
Boolean::Constant(b) => {
|
||||
assert!(input_len == 0);
|
||||
|
||||
@@ -2,22 +2,19 @@ use algebra_core::Field;
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crate::{prf::PRF, Vec};
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::{ConstraintSystemRef, SynthesisError};
|
||||
|
||||
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
|
||||
+ 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>;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub use self::blake2s::*;
|
||||
|
||||
pub trait PRF {
|
||||
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;
|
||||
|
||||
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>;
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
use algebra_core::Field;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_core::SynthesisError;
|
||||
use r1cs_std::prelude::*;
|
||||
|
||||
use crate::signature::SignatureScheme;
|
||||
|
||||
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>
|
||||
+ AllocGadget<S::PublicKey, ConstraintF>
|
||||
+ AllocVar<S::PublicKey, ConstraintF>
|
||||
+ 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>;
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ pub trait SignatureScheme {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{signature::schnorr::SchnorrSignature, SignatureScheme};
|
||||
use crate::signature::{schnorr, *};
|
||||
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,
|
||||
};
|
||||
use blake2::Blake2s;
|
||||
@@ -90,13 +90,13 @@ mod test {
|
||||
fn schnorr_signature_test() {
|
||||
let message = "Hi, I am a Schnorr signature!";
|
||||
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(),
|
||||
"Bad message".as_bytes(),
|
||||
);
|
||||
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(),
|
||||
&random_scalar.as_slice(),
|
||||
);
|
||||
|
||||
@@ -1,242 +1,157 @@
|
||||
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 crate::signature::SigRandomizePkGadget;
|
||||
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use crate::signature::schnorr::{SchnorrPublicKey, SchnorrSigParameters, SchnorrSignature};
|
||||
use crate::signature::schnorr::{Parameters, PublicKey, Schnorr};
|
||||
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)]
|
||||
#[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>,
|
||||
#[doc(hidden)]
|
||||
_engine: PhantomData<*const ConstraintF>,
|
||||
}
|
||||
|
||||
pub struct SchnorrRandomizePkGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
|
||||
#[doc(hidden)]
|
||||
_group: PhantomData<*const G>,
|
||||
#[doc(hidden)]
|
||||
_group_gadget: PhantomData<*const GG>,
|
||||
#[doc(hidden)]
|
||||
_engine: PhantomData<*const ConstraintF>,
|
||||
}
|
||||
|
||||
impl<G, GG, D, ConstraintF> SigRandomizePkGadget<SchnorrSignature<G, D>, ConstraintF>
|
||||
for SchnorrRandomizePkGadget<G, ConstraintF, GG>
|
||||
pub struct PublicKeyVar<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
||||
where
|
||||
G: Group,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
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>;
|
||||
pub_key: GC,
|
||||
#[doc(hidden)]
|
||||
_group: PhantomData<*const C>,
|
||||
}
|
||||
|
||||
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
mut cs: CS,
|
||||
parameters: &Self::ParametersGadget,
|
||||
public_key: &Self::PublicKeyGadget,
|
||||
randomness: &[UInt8],
|
||||
) -> Result<Self::PublicKeyGadget, SynthesisError> {
|
||||
pub struct SchnorrRandomizePkGadget<C: ProjectiveCurve, GC: CurveVar<C, ConstraintF<C>>>
|
||||
where
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
#[doc(hidden)]
|
||||
_group: PhantomData<*const C>,
|
||||
#[doc(hidden)]
|
||||
_group_gadget: PhantomData<*const GC>,
|
||||
}
|
||||
|
||||
impl<C, GC, D> SigRandomizePkGadget<Schnorr<C, D>, ConstraintF<C>>
|
||||
for SchnorrRandomizePkGadget<C, GC>
|
||||
where
|
||||
C: ProjectiveCurve,
|
||||
GC: CurveVar<C, ConstraintF<C>>,
|
||||
D: Digest + Send + Sync,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
type ParametersVar = ParametersVar<C, GC>;
|
||||
type PublicKeyVar = PublicKeyVar<C, GC>;
|
||||
|
||||
fn randomize(
|
||||
parameters: &Self::ParametersVar,
|
||||
public_key: &Self::PublicKeyVar,
|
||||
randomness: &[UInt8<ConstraintF<C>>],
|
||||
) -> Result<Self::PublicKeyVar, SynthesisError> {
|
||||
let base = parameters.generator.clone();
|
||||
let randomness = randomness
|
||||
.iter()
|
||||
.flat_map(|b| b.into_bits_le())
|
||||
.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,
|
||||
_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
|
||||
G: Group,
|
||||
ConstraintF: Field,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
C: ProjectiveCurve,
|
||||
GC: CurveVar<C, ConstraintF<C>>,
|
||||
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 {
|
||||
generator,
|
||||
_engine: PhantomData,
|
||||
_group: PhantomData,
|
||||
_curve: 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))?;
|
||||
impl<C, GC> AllocVar<PublicKey<C>, ConstraintF<C>> for PublicKeyVar<C, GC>
|
||||
where
|
||||
C: ProjectiveCurve,
|
||||
GC: CurveVar<C, ConstraintF<C>>,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
fn new_variable<T: Borrow<PublicKey<C>>>(
|
||||
cs: impl Into<Namespace<ConstraintF<C>>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
) -> Result<Self, SynthesisError> {
|
||||
let pub_key = GC::new_variable(cs, f, mode)?;
|
||||
Ok(Self {
|
||||
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,
|
||||
pub_key,
|
||||
_group: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, ConstraintF, GG> AllocGadget<SchnorrPublicKey<G>, ConstraintF>
|
||||
for SchnorrSigGadgetPk<G, ConstraintF, GG>
|
||||
impl<C, GC> EqGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
|
||||
where
|
||||
G: Group,
|
||||
ConstraintF: Field,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
{
|
||||
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())?;
|
||||
Ok(Self {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, ConstraintF, GG> ConditionalEqGadget<ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
|
||||
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]
|
||||
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,
|
||||
mut cs: CS,
|
||||
other: &Self,
|
||||
condition: &Boolean,
|
||||
condition: &Boolean<ConstraintF<C>>,
|
||||
) -> 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()
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
G: Group,
|
||||
ConstraintF: Field,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
{
|
||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
||||
#[inline]
|
||||
fn conditional_enforce_not_equal(
|
||||
&self,
|
||||
mut cs: CS,
|
||||
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
self.pub_key.to_bytes(&mut cs.ns(|| "PubKey To Bytes"))
|
||||
other: &Self,
|
||||
condition: &Boolean<ConstraintF<C>>,
|
||||
) -> Result<(), SynthesisError> {
|
||||
self.pub_key
|
||||
.conditional_enforce_not_equal(&other.pub_key, condition)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, GC> ToBytesGadget<ConstraintF<C>> for PublicKeyVar<C, GC>
|
||||
where
|
||||
C: ProjectiveCurve,
|
||||
GC: CurveVar<C, ConstraintF<C>>,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF<C>>>, SynthesisError> {
|
||||
self.pub_key.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ use crate::{Error, SignatureScheme, Vec};
|
||||
use algebra_core::{
|
||||
bytes::ToBytes,
|
||||
fields::{Field, PrimeField},
|
||||
groups::Group,
|
||||
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 digest::Digest;
|
||||
@@ -13,57 +12,55 @@ use rand::Rng;
|
||||
#[cfg(feature = "r1cs")]
|
||||
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>,
|
||||
}
|
||||
|
||||
#[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>,
|
||||
pub generator: G,
|
||||
pub generator: C::Affine,
|
||||
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]
|
||||
fn write<W: Write>(&self, writer: W) -> IoResult<()> {
|
||||
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
|
||||
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> {
|
||||
let setup_time = start_timer!(|| "SchnorrSig::Setup");
|
||||
|
||||
let mut salt = [0u8; 32];
|
||||
rng.fill_bytes(&mut salt);
|
||||
let generator = G::rand(rng);
|
||||
let generator = C::rand(rng).into();
|
||||
|
||||
end_timer!(setup_time);
|
||||
Ok(SchnorrSigParameters {
|
||||
Ok(Parameters {
|
||||
_hash: PhantomData,
|
||||
generator,
|
||||
salt,
|
||||
@@ -76,11 +73,11 @@ where
|
||||
) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
|
||||
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);
|
||||
Ok((public_key, SchnorrSecretKey(secret_key)))
|
||||
Ok((public_key, SecretKey(secret_key)))
|
||||
}
|
||||
|
||||
fn sign<R: Rng>(
|
||||
@@ -93,10 +90,10 @@ where
|
||||
// (k, e);
|
||||
let (random_scalar, verifier_challenge) = loop {
|
||||
// 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.
|
||||
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.
|
||||
let mut hash_input = Vec::new();
|
||||
@@ -106,7 +103,7 @@ where
|
||||
|
||||
// Compute the supposed verifier response: e := H(salt || r || msg);
|
||||
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);
|
||||
};
|
||||
@@ -114,7 +111,7 @@ where
|
||||
|
||||
// k - xe;
|
||||
let prover_response = random_scalar - &(verifier_challenge * &sk.0);
|
||||
let signature = SchnorrSig {
|
||||
let signature = Signature {
|
||||
prover_response,
|
||||
verifier_challenge,
|
||||
};
|
||||
@@ -131,13 +128,14 @@ where
|
||||
) -> Result<bool, Error> {
|
||||
let verify_time = start_timer!(|| "SchnorrSig::Verify");
|
||||
|
||||
let SchnorrSig {
|
||||
let Signature {
|
||||
prover_response,
|
||||
verifier_challenge,
|
||||
} = signature;
|
||||
let mut claimed_prover_commitment = parameters.generator.mul(prover_response);
|
||||
let public_key_times_verifier_challenge = pk.mul(verifier_challenge);
|
||||
let mut claimed_prover_commitment = parameters.generator.mul(*prover_response);
|
||||
let public_key_times_verifier_challenge = pk.mul(*verifier_challenge);
|
||||
claimed_prover_commitment += &public_key_times_verifier_challenge;
|
||||
let claimed_prover_commitment = claimed_prover_commitment.into_affine();
|
||||
|
||||
let mut hash_input = Vec::new();
|
||||
hash_input.extend_from_slice(¶meters.salt);
|
||||
@@ -145,7 +143,7 @@ where
|
||||
hash_input.extend_from_slice(&message);
|
||||
|
||||
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
|
||||
} else {
|
||||
@@ -162,20 +160,26 @@ where
|
||||
) -> Result<Self::PublicKey, Error> {
|
||||
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) {
|
||||
if bit {
|
||||
encoded += &base;
|
||||
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 {
|
||||
encoded.add_assign_mixed(&base)
|
||||
}
|
||||
base.double_in_place();
|
||||
}
|
||||
randomized_pk += &encoded;
|
||||
encoded.add_assign_mixed(&randomized_pk);
|
||||
|
||||
end_timer!(rand_pk_time);
|
||||
|
||||
Ok(randomized_pk)
|
||||
Ok(encoded.into())
|
||||
}
|
||||
|
||||
fn randomize_signature(
|
||||
@@ -184,12 +188,12 @@ where
|
||||
randomness: &[u8],
|
||||
) -> Result<Self::Signature, Error> {
|
||||
let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig");
|
||||
let SchnorrSig {
|
||||
let Signature {
|
||||
prover_response,
|
||||
verifier_challenge,
|
||||
} = 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) {
|
||||
if bit {
|
||||
multiplier += &base;
|
||||
@@ -197,7 +201,7 @@ where
|
||||
base.double_in_place();
|
||||
}
|
||||
|
||||
let new_sig = SchnorrSig {
|
||||
let new_sig = Signature {
|
||||
prover_response: *prover_response - &(*verifier_challenge * &multiplier),
|
||||
verifier_challenge: *verifier_challenge,
|
||||
};
|
||||
@@ -217,11 +221,11 @@ pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
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]
|
||||
fn to_field_elements(&self) -> Result<Vec<ConstraintF>, Error> {
|
||||
self.generator.to_field_elements()
|
||||
self.generator.into_projective().to_field_elements()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user