@ -0,0 +1,29 @@ |
|||||
|
[package] |
||||
|
name = "crypto-primitives" |
||||
|
version = "0.1.0" |
||||
|
authors = ["Pratyush Mishra <pratyushmishra@berkeley.edu>"] |
||||
|
edition = "2018" |
||||
|
|
||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
|
||||
|
[dependencies] |
||||
|
algebra = { path = "../algebra" } |
||||
|
r1cs-core = { path = "../r1cs-core", optional = true } |
||||
|
r1cs-std = { path = "../r1cs-std", optional = true } |
||||
|
gm17 = { path = "../gm17", optional = true } |
||||
|
bench-utils = { path = "../bench-utils" } |
||||
|
|
||||
|
digest = "0.7" |
||||
|
blake2 = "0.7" |
||||
|
|
||||
|
rand = { version = "0.7" } |
||||
|
derivative = "1" |
||||
|
rayon = "1" |
||||
|
|
||||
|
[features] |
||||
|
r1cs = [ "r1cs-core", "r1cs-std" ] |
||||
|
|
||||
|
[dev-dependencies] |
||||
|
criterion = "0.2" |
||||
|
rand_xorshift = { version = "0.2" } |
||||
|
|
@ -0,0 +1,169 @@ |
|||||
|
use crate::crypto_primitives::commitment::blake2s::Blake2sCommitment;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use crate::gadgets::{
|
||||
|
prf::blake2s::{blake2s_gadget, Blake2sOutputGadget},
|
||||
|
CommitmentGadget,
|
||||
|
};
|
||||
|
use algebra::{PrimeField, Field};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use std::borrow::Borrow;
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub struct Blake2sParametersGadget;
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub struct Blake2sRandomnessGadget(pub Vec<UInt8>);
|
||||
|
|
||||
|
pub struct Blake2sCommitmentGadget;
|
||||
|
|
||||
|
impl<ConstraintF: PrimeField> CommitmentGadget<Blake2sCommitment, ConstraintF> for Blake2sCommitmentGadget {
|
||||
|
type OutputGadget = Blake2sOutputGadget;
|
||||
|
type ParametersGadget = Blake2sParametersGadget;
|
||||
|
type RandomnessGadget = Blake2sRandomnessGadget;
|
||||
|
|
||||
|
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
_: &Self::ParametersGadget,
|
||||
|
input: &[UInt8],
|
||||
|
r: &Self::RandomnessGadget,
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
|
let mut input_bits = Vec::with_capacity(512);
|
||||
|
for byte in input.into_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)))?;
|
||||
|
result.extend_from_slice(&chunk);
|
||||
|
}
|
||||
|
Ok(Blake2sOutputGadget(result))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: Field> AllocGadget<(), ConstraintF> for Blake2sParametersGadget {
|
||||
|
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: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sRandomnessGadget {
|
||||
|
#[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))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use algebra::fields::bls12_381::Fr;
|
||||
|
use rand::{thread_rng, Rng};
|
||||
|
|
||||
|
use crate::{
|
||||
|
crypto_primitives::commitment::{blake2s::Blake2sCommitment, CommitmentScheme},
|
||||
|
gadgets::commitment::{
|
||||
|
blake2s::{Blake2sCommitmentGadget, Blake2sRandomnessGadget},
|
||||
|
CommitmentGadget,
|
||||
|
},
|
||||
|
};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
use r1cs_std::test_constraint_system::TestConstraintSystem;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn commitment_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let input = [1u8; 32];
|
||||
|
|
||||
|
let rng = &mut thread_rng();
|
||||
|
|
||||
|
type TestCOMM = Blake2sCommitment;
|
||||
|
type TestCOMMGadget = Blake2sCommitmentGadget;
|
||||
|
|
||||
|
let mut randomness = [0u8; 32];
|
||||
|
rng.fill(&mut randomness);
|
||||
|
|
||||
|
let parameters = ();
|
||||
|
let primitive_result = Blake2sCommitment::commit(¶meters, &input, &randomness).unwrap();
|
||||
|
|
||||
|
let mut input_bytes = vec![];
|
||||
|
for (byte_i, input_byte) in input.into_iter().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
|
||||
|
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
|
||||
|
}
|
||||
|
|
||||
|
let mut randomness_bytes = vec![];
|
||||
|
for (byte_i, random_byte) in randomness.into_iter().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("randomness_byte_gadget_{}", byte_i));
|
||||
|
randomness_bytes.push(UInt8::alloc(cs, || Ok(*random_byte)).unwrap());
|
||||
|
}
|
||||
|
let randomness_bytes = Blake2sRandomnessGadget(randomness_bytes);
|
||||
|
|
||||
|
let gadget_parameters =
|
||||
|
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fr>>::ParametersGadget::alloc(
|
||||
|
&mut 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();
|
||||
|
|
||||
|
for i in 0..32 {
|
||||
|
assert_eq!(primitive_result[i], gadget_result.0[i].get_value().unwrap());
|
||||
|
}
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,30 @@ |
|||||
|
use super::CommitmentScheme;
|
||||
|
use blake2::Blake2s as b2s;
|
||||
|
use digest::Digest;
|
||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
|
||||
|
pub struct Blake2sCommitment;
|
||||
|
|
||||
|
impl CommitmentScheme for Blake2sCommitment {
|
||||
|
type Parameters = ();
|
||||
|
type Randomness = [u8; 32];
|
||||
|
type Output = [u8; 32];
|
||||
|
|
||||
|
fn setup<R: Rng>(_: &mut R) -> Result<Self::Parameters, Error> {
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
fn commit(
|
||||
|
_: &Self::Parameters,
|
||||
|
input: &[u8],
|
||||
|
randomness: &Self::Randomness,
|
||||
|
) -> Result<Self::Output, Error> {
|
||||
|
let mut h = b2s::new();
|
||||
|
h.input(input);
|
||||
|
h.input(randomness.as_ref());
|
||||
|
let mut result = [0u8; 32];
|
||||
|
result.copy_from_slice(&h.result());
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,23 @@ |
|||||
|
use crate::CommitmentScheme;
|
||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
pub trait CommitmentGadget<C: CommitmentScheme, ConstraintF: Field> {
|
||||
|
type OutputGadget: EqGadget<ConstraintF>
|
||||
|
+ ToBytesGadget<ConstraintF>
|
||||
|
+ AllocGadget<C::Output, ConstraintF>
|
||||
|
+ Clone
|
||||
|
+ Sized
|
||||
|
+ Debug;
|
||||
|
type ParametersGadget: AllocGadget<C::Parameters, ConstraintF> + Clone;
|
||||
|
type RandomnessGadget: AllocGadget<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>;
|
||||
|
}
|
@ -0,0 +1,59 @@ |
|||||
|
use algebra::{Field, PrimeField};
|
||||
|
|
||||
|
use crate::commitment::{
|
||||
|
injective_map::{InjectiveMap, PedersenCommCompressor},
|
||||
|
pedersen::PedersenWindow,
|
||||
|
pedersen::constraints::{
|
||||
|
PedersenCommitmentGadget, PedersenCommitmentGadgetParameters, PedersenRandomnessGadget,
|
||||
|
},
|
||||
|
CommitmentGadget,
|
||||
|
};
|
||||
|
|
||||
|
pub use crate::crh::injective_map::constraints::InjectiveMapGadget;
|
||||
|
use algebra::groups::Group;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::{groups::GroupGadget, uint8::UInt8};
|
||||
|
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
pub struct PedersenCommitmentCompressorGadget<
|
||||
|
G: Group,
|
||||
|
I: InjectiveMap<G>,
|
||||
|
ConstraintF: Field,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
|
||||
|
> {
|
||||
|
_compressor: PhantomData<I>,
|
||||
|
_compressor_gadget: PhantomData<IG>,
|
||||
|
_crh: PedersenCommitmentGadget<G, ConstraintF, GG>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G, I, ConstraintF, GG, IG, W> CommitmentGadget<PedersenCommCompressor<G, I, W>, ConstraintF>
|
||||
|
for PedersenCommitmentCompressorGadget<G, I, ConstraintF, GG, IG>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
I: InjectiveMap<G>,
|
||||
|
ConstraintF: PrimeField,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
|
||||
|
W: PedersenWindow,
|
||||
|
{
|
||||
|
type OutputGadget = IG::OutputGadget;
|
||||
|
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
|
||||
|
type RandomnessGadget = PedersenRandomnessGadget;
|
||||
|
|
||||
|
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)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,47 @@ |
|||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
use super::{
|
||||
|
pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness, PedersenWindow},
|
||||
|
CommitmentScheme,
|
||||
|
};
|
||||
|
pub use crate::crh::injective_map::InjectiveMap;
|
||||
|
use algebra::groups::Group;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
pub struct PedersenCommCompressor<G: Group, I: InjectiveMap<G>, W: PedersenWindow> {
|
||||
|
_group: PhantomData<G>,
|
||||
|
_compressor: PhantomData<I>,
|
||||
|
_comm: PedersenCommitment<G, W>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> CommitmentScheme
|
||||
|
for PedersenCommCompressor<G, I, W>
|
||||
|
{
|
||||
|
type Output = I::Output;
|
||||
|
type Parameters = PedersenParameters<G>;
|
||||
|
type Randomness = PedersenRandomness<G>;
|
||||
|
|
||||
|
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);
|
||||
|
end_timer!(time);
|
||||
|
params
|
||||
|
}
|
||||
|
|
||||
|
fn commit(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
input: &[u8],
|
||||
|
randomness: &Self::Randomness,
|
||||
|
) -> Result<Self::Output, Error> {
|
||||
|
let eval_time = start_timer!(|| "PedersenCompressor::Eval");
|
||||
|
let result = I::injective_map(&PedersenCommitment::<G, W>::commit(
|
||||
|
parameters, input, randomness,
|
||||
|
)?)?;
|
||||
|
end_timer!(eval_time);
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,30 @@ |
|||||
|
use rand::Rng;
|
||||
|
use algebra::UniformRand;
|
||||
|
use std::{fmt::Debug, hash::Hash};
|
||||
|
|
||||
|
use algebra::bytes::ToBytes;
|
||||
|
|
||||
|
pub mod blake2s;
|
||||
|
pub mod injective_map;
|
||||
|
pub mod pedersen;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub use constraints::*;
|
||||
|
|
||||
|
use crate::Error;
|
||||
|
|
||||
|
pub trait CommitmentScheme {
|
||||
|
type Output: ToBytes + Clone + Default + Eq + Hash + Debug;
|
||||
|
type Parameters: Clone;
|
||||
|
type Randomness: Clone + ToBytes + Default + Eq + UniformRand + Debug;
|
||||
|
|
||||
|
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
|
||||
|
|
||||
|
fn commit(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
input: &[u8],
|
||||
|
r: &Self::Randomness,
|
||||
|
) -> Result<Self::Output, Error>;
|
||||
|
}
|
@ -0,0 +1,244 @@ |
|||||
|
use crate::{
|
||||
|
commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness},
|
||||
|
crh::pedersen::PedersenWindow,
|
||||
|
};
|
||||
|
use algebra::{to_bytes, Group, ToBytes};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use crate::commitment::CommitmentGadget;
|
||||
|
use algebra::fields::{Field, PrimeField};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))]
|
||||
|
pub struct PedersenCommitmentGadgetParameters<G: Group, W: PedersenWindow, ConstraintF: Field> {
|
||||
|
params: PedersenParameters<G>,
|
||||
|
#[doc(hidden)]
|
||||
|
_group: PhantomData<G>,
|
||||
|
#[doc(hidden)]
|
||||
|
_engine: PhantomData<ConstraintF>,
|
||||
|
#[doc(hidden)]
|
||||
|
_window: PhantomData<W>,
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct PedersenRandomnessGadget(Vec<UInt8>);
|
||||
|
|
||||
|
pub struct PedersenCommitmentGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>(
|
||||
|
#[doc(hidden)]
|
||||
|
PhantomData<*const G>,
|
||||
|
#[doc(hidden)]
|
||||
|
PhantomData<*const GG>,
|
||||
|
PhantomData<ConstraintF>,
|
||||
|
);
|
||||
|
|
||||
|
impl<ConstraintF, G, GG, W> CommitmentGadget<PedersenCommitment<G, W>, ConstraintF>
|
||||
|
for PedersenCommitmentGadget<G, ConstraintF, GG>
|
||||
|
where
|
||||
|
ConstraintF: PrimeField,
|
||||
|
G: Group,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
W: PedersenWindow,
|
||||
|
{
|
||||
|
type OutputGadget = GG;
|
||||
|
type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
|
||||
|
type RandomnessGadget = PedersenRandomnessGadget;
|
||||
|
|
||||
|
fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
input: &[UInt8],
|
||||
|
r: &Self::RandomnessGadget,
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
|
assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
|
||||
|
|
||||
|
let mut padded_input = input.to_vec();
|
||||
|
// Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`.
|
||||
|
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
let current_length = input.len();
|
||||
|
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
|
||||
|
padded_input.push(UInt8::constant(0u8));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
|
||||
|
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
|
||||
|
|
||||
|
// Allocate new variable for commitment output.
|
||||
|
let input_in_bits: Vec<_> = padded_input
|
||||
|
.iter()
|
||||
|
.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,
|
||||
|
)?;
|
||||
|
|
||||
|
// 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),
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G, W, ConstraintF> AllocGadget<PedersenParameters<G>, ConstraintF> for PedersenCommitmentGadgetParameters<G, W, ConstraintF>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
W: PedersenWindow,
|
||||
|
ConstraintF: PrimeField,
|
||||
|
{
|
||||
|
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()?;
|
||||
|
let parameters = temp.borrow().clone();
|
||||
|
|
||||
|
Ok(PedersenCommitmentGadgetParameters {
|
||||
|
params: parameters,
|
||||
|
_group: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
_window: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
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,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G, ConstraintF> AllocGadget<PedersenRandomness<G>, ConstraintF> for PedersenRandomnessGadget
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
ConstraintF: PrimeField,
|
||||
|
{
|
||||
|
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,
|
||||
|
)?))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use algebra::{fields::jubjub::{fq::Fq, fr::Fr}};
|
||||
|
use rand::thread_rng;
|
||||
|
use algebra::UniformRand;
|
||||
|
|
||||
|
use crate::{
|
||||
|
commitment::{
|
||||
|
pedersen::{PedersenCommitment, PedersenRandomness, constraints::PedersenCommitmentGadget},
|
||||
|
CommitmentScheme,
|
||||
|
CommitmentGadget,
|
||||
|
},
|
||||
|
crh::pedersen::PedersenWindow,
|
||||
|
};
|
||||
|
use algebra::curves::{jubjub::JubJubProjective as JubJub, ProjectiveCurve};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
use r1cs_std::{
|
||||
|
groups::jubjub::JubJubGadget, test_constraint_system::TestConstraintSystem, prelude::*,
|
||||
|
};
|
||||
|
|
||||
|
#[test]
|
||||
|
fn commitment_gadget_test() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
|
pub(super) struct Window;
|
||||
|
|
||||
|
impl PedersenWindow for Window {
|
||||
|
const WINDOW_SIZE: usize = 4;
|
||||
|
const NUM_WINDOWS: usize = 8;
|
||||
|
}
|
||||
|
|
||||
|
let input = [1u8; 4];
|
||||
|
|
||||
|
let rng = &mut thread_rng();
|
||||
|
|
||||
|
type TestCOMM = PedersenCommitment<JubJub, Window>;
|
||||
|
type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
|
||||
|
|
||||
|
let randomness = PedersenRandomness(Fr::rand(rng));
|
||||
|
|
||||
|
let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
|
||||
|
let primitive_result =
|
||||
|
PedersenCommitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
|
||||
|
|
||||
|
let mut input_bytes = vec![];
|
||||
|
for (byte_i, input_byte) in input.into_iter().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
|
||||
|
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
|
||||
|
}
|
||||
|
|
||||
|
let randomness =
|
||||
|
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
|
||||
|
&mut cs.ns(|| "gadget_randomness"),
|
||||
|
|| Ok(&randomness),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let gadget_parameters =
|
||||
|
<TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
|
||||
|
&mut 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 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());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,123 @@ |
|||||
|
use crate::Error;
|
||||
|
use algebra::UniformRand;
|
||||
|
use rand::Rng;
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
use super::CommitmentScheme;
|
||||
|
use algebra::{bytes::ToBytes, groups::Group, BitIterator, FpParameters, PrimeField};
|
||||
|
use std::io::{Result as IoResult, Write};
|
||||
|
|
||||
|
pub use crate::crh::pedersen::PedersenWindow;
|
||||
|
use crate::crh::{
|
||||
|
pedersen::{PedersenCRH, PedersenParameters as PedersenCRHParameters},
|
||||
|
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 PedersenCommitment<G: Group, W: PedersenWindow> {
|
||||
|
group: PhantomData<G>,
|
||||
|
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);
|
||||
|
|
||||
|
impl<G: Group> UniformRand for PedersenRandomness<G> {
|
||||
|
#[inline]
|
||||
|
fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||
|
PedersenRandomness(UniformRand::rand(rng))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group> ToBytes for PedersenRandomness<G> {
|
||||
|
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;
|
||||
|
|
||||
|
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
||||
|
let time = start_timer!(|| format!(
|
||||
|
"PedersenCOMM::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
|
||||
|
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);
|
||||
|
end_timer!(time);
|
||||
|
|
||||
|
Ok(Self::Parameters {
|
||||
|
randomness_generator,
|
||||
|
generators,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn commit(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
input: &[u8],
|
||||
|
randomness: &Self::Randomness,
|
||||
|
) -> Result<Self::Output, Error> {
|
||||
|
let commit_time = start_timer!(|| "PedersenCOMM::Commit");
|
||||
|
// If the input is too long, return an error.
|
||||
|
if input.len() > W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
panic!("incorrect input length: {:?}", input.len());
|
||||
|
}
|
||||
|
// Pad the input to the necessary length.
|
||||
|
let mut padded_input = Vec::with_capacity(input.len());
|
||||
|
let mut input = input;
|
||||
|
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
let current_length = input.len();
|
||||
|
padded_input.extend_from_slice(input);
|
||||
|
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
|
||||
|
padded_input.push(0u8);
|
||||
|
}
|
||||
|
input = padded_input.as_slice();
|
||||
|
}
|
||||
|
assert_eq!(parameters.generators.len(), W::NUM_WINDOWS);
|
||||
|
|
||||
|
// Invoke Pedersen CRH here, to prevent code duplication.
|
||||
|
|
||||
|
let crh_parameters = PedersenCRHParameters {
|
||||
|
generators: parameters.generators.clone(),
|
||||
|
};
|
||||
|
let mut result = PedersenCRH::<_, W>::evaluate(&crh_parameters, &input)?;
|
||||
|
let randomize_time = start_timer!(|| "Randomize");
|
||||
|
|
||||
|
// Compute h^r.
|
||||
|
let mut scalar_bits = BitIterator::new(randomness.0.into_repr()).collect::<Vec<_>>();
|
||||
|
scalar_bits.reverse();
|
||||
|
for (bit, power) in scalar_bits
|
||||
|
.into_iter()
|
||||
|
.zip(¶meters.randomness_generator)
|
||||
|
{
|
||||
|
if bit {
|
||||
|
result += power
|
||||
|
}
|
||||
|
}
|
||||
|
end_timer!(randomize_time);
|
||||
|
end_timer!(commit_time);
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,25 @@ |
|||||
|
use algebra::Field;
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
use crate::crh::FixedLengthCRH;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
pub trait FixedLengthCRHGadget<H: FixedLengthCRH, ConstraintF: Field>: Sized {
|
||||
|
type OutputGadget: ConditionalEqGadget<ConstraintF>
|
||||
|
+ EqGadget<ConstraintF>
|
||||
|
+ ToBytesGadget<ConstraintF>
|
||||
|
+ CondSelectGadget<ConstraintF>
|
||||
|
+ AllocGadget<H::Output, 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>;
|
||||
|
}
|
@ -0,0 +1,115 @@ |
|||||
|
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
|
||||
|
use crate::crh::{
|
||||
|
FixedLengthCRHGadget,
|
||||
|
injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor},
|
||||
|
pedersen::{
|
||||
|
PedersenWindow,
|
||||
|
constraints::{PedersenCRHGadget, PedersenCRHGadgetParameters},
|
||||
|
}
|
||||
|
};
|
||||
|
|
||||
|
use algebra::{
|
||||
|
curves::{
|
||||
|
models::{ModelParameters, TEModelParameters},
|
||||
|
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
|
||||
|
},
|
||||
|
fields::{Field, PrimeField, SquareRootField},
|
||||
|
groups::Group,
|
||||
|
};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::{
|
||||
|
fields::fp::FpGadget,
|
||||
|
groups::{curves::twisted_edwards::AffineGadget as TwistedEdwardsGadget, GroupGadget},
|
||||
|
prelude::*,
|
||||
|
};
|
||||
|
|
||||
|
pub trait InjectiveMapGadget<G: Group, I: InjectiveMap<G>, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
|
||||
|
{
|
||||
|
type OutputGadget: EqGadget<ConstraintF>
|
||||
|
+ ToBytesGadget<ConstraintF>
|
||||
|
+ CondSelectGadget<ConstraintF>
|
||||
|
+ AllocGadget<I::Output, ConstraintF>
|
||||
|
+ Debug
|
||||
|
+ Clone
|
||||
|
+ Sized;
|
||||
|
|
||||
|
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
ge: &GG,
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
pub struct TECompressorGadget;
|
||||
|
|
||||
|
impl<ConstraintF, P> InjectiveMapGadget<TEAffine<P>, TECompressor, ConstraintF, TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>>
|
||||
|
for TECompressorGadget
|
||||
|
where
|
||||
|
ConstraintF: PrimeField + SquareRootField,
|
||||
|
P: TEModelParameters + ModelParameters<BaseField = ConstraintF>,
|
||||
|
{
|
||||
|
type OutputGadget = FpGadget<ConstraintF>;
|
||||
|
|
||||
|
fn evaluate_map<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
_cs: CS,
|
||||
|
ge: &TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>,
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
|
Ok(ge.x.clone())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF, P>
|
||||
|
InjectiveMapGadget<TEProjective<P>, TECompressor, ConstraintF, TwistedEdwardsGadget<P, ConstraintF, FpGadget<ConstraintF>>>
|
||||
|
for TECompressorGadget
|
||||
|
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: Group,
|
||||
|
I: InjectiveMap<G>,
|
||||
|
ConstraintF: Field,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
|
||||
|
> {
|
||||
|
_compressor: PhantomData<I>,
|
||||
|
_compressor_gadget: PhantomData<IG>,
|
||||
|
_crh: PedersenCRHGadget<G, ConstraintF, GG>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G, I, ConstraintF, GG, IG, W> FixedLengthCRHGadget<PedersenCRHCompressor<G, I, W>, ConstraintF>
|
||||
|
for PedersenCRHCompressorGadget<G, I, ConstraintF, GG, IG>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
I: InjectiveMap<G>,
|
||||
|
ConstraintF: Field,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
IG: InjectiveMapGadget<G, I, ConstraintF, GG>,
|
||||
|
W: PedersenWindow,
|
||||
|
{
|
||||
|
type OutputGadget = IG::OutputGadget;
|
||||
|
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, 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)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,76 @@ |
|||||
|
use crate::CryptoError;
|
||||
|
use algebra::bytes::ToBytes;
|
||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
use std::{fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
|
|
||||
|
use super::{
|
||||
|
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
|
||||
|
FixedLengthCRH,
|
||||
|
};
|
||||
|
use algebra::{
|
||||
|
curves::{
|
||||
|
models::{ModelParameters, TEModelParameters},
|
||||
|
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective},
|
||||
|
ProjectiveCurve,
|
||||
|
},
|
||||
|
groups::Group,
|
||||
|
};
|
||||
|
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
pub trait InjectiveMap<G: Group> {
|
||||
|
type Output: ToBytes + Clone + Eq + Hash + Default + Debug;
|
||||
|
fn injective_map(ge: &G) -> Result<Self::Output, CryptoError>;
|
||||
|
}
|
||||
|
|
||||
|
pub struct TECompressor;
|
||||
|
|
||||
|
impl<P: TEModelParameters> InjectiveMap<TEAffine<P>> for TECompressor {
|
||||
|
type Output = <P as ModelParameters>::BaseField;
|
||||
|
|
||||
|
fn injective_map(ge: &TEAffine<P>) -> Result<Self::Output, CryptoError> {
|
||||
|
debug_assert!(ge.is_in_correct_subgroup_assuming_on_curve());
|
||||
|
Ok(ge.x)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: TEModelParameters> InjectiveMap<TEProjective<P>> for TECompressor {
|
||||
|
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>,
|
||||
|
_compressor: PhantomData<I>,
|
||||
|
_crh: PedersenCRH<G, W>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, I: InjectiveMap<G>, W: PedersenWindow> FixedLengthCRH
|
||||
|
for PedersenCRHCompressor<G, I, W>
|
||||
|
{
|
||||
|
const INPUT_SIZE_BITS: usize = PedersenCRH::<G, W>::INPUT_SIZE_BITS;
|
||||
|
type Output = I::Output;
|
||||
|
type Parameters = PedersenParameters<G>;
|
||||
|
|
||||
|
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);
|
||||
|
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)?)?;
|
||||
|
end_timer!(eval_time);
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,24 @@ |
|||||
|
use algebra::bytes::ToBytes;
|
||||
|
use rand::Rng;
|
||||
|
use std::hash::Hash;
|
||||
|
|
||||
|
pub mod injective_map;
|
||||
|
pub mod pedersen;
|
||||
|
|
||||
|
use crate::Error;
|
||||
|
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub use constraints::*;
|
||||
|
|
||||
|
|
||||
|
pub trait FixedLengthCRH {
|
||||
|
const INPUT_SIZE_BITS: usize;
|
||||
|
type Output: ToBytes + Clone + Eq + Hash + Default;
|
||||
|
type Parameters: Clone + Default;
|
||||
|
|
||||
|
fn setup<R: Rng>(r: &mut R) -> Result<Self::Parameters, Error>;
|
||||
|
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error>;
|
||||
|
}
|
@ -0,0 +1,193 @@ |
|||||
|
use algebra::Field;
|
||||
|
|
||||
|
use crate::crh::{
|
||||
|
FixedLengthCRHGadget,
|
||||
|
pedersen::{PedersenCRH, PedersenParameters, PedersenWindow},
|
||||
|
};
|
||||
|
use algebra::groups::Group;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use std::{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>
|
||||
|
where
|
||||
|
ConstraintF: Field,
|
||||
|
G: Group,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
W: PedersenWindow,
|
||||
|
{
|
||||
|
type OutputGadget = GG;
|
||||
|
type ParametersGadget = PedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
|
||||
|
|
||||
|
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
input: &[UInt8],
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
|
let mut padded_input = input.to_vec();
|
||||
|
// Pad the input if it is not the current length.
|
||||
|
if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
let current_length = input.len();
|
||||
|
for _ in current_length..(W::WINDOW_SIZE * W::NUM_WINDOWS / 8) {
|
||||
|
padded_input.push(UInt8::constant(0u8));
|
||||
|
}
|
||||
|
}
|
||||
|
assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
|
||||
|
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
|
||||
|
|
||||
|
// Allocate new variable for the result.
|
||||
|
let input_in_bits: Vec<_> = padded_input
|
||||
|
.iter()
|
||||
|
.flat_map(|byte| byte.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)?;
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
|
||||
|
AllocGadget<PedersenParameters<G>, ConstraintF> for PedersenCRHGadgetParameters<G, W, ConstraintF, GG>
|
||||
|
{
|
||||
|
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();
|
||||
|
Ok(PedersenCRHGadgetParameters {
|
||||
|
params,
|
||||
|
_group_g: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
_window: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
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 algebra::fields::bls12_381::fr::Fr;
|
||||
|
use rand::{thread_rng, Rng};
|
||||
|
|
||||
|
use crate::crh::{
|
||||
|
pedersen::{PedersenCRH, PedersenWindow},
|
||||
|
pedersen::constraints::PedersenCRHGadget,
|
||||
|
FixedLengthCRH,
|
||||
|
FixedLengthCRHGadget
|
||||
|
};
|
||||
|
use algebra::curves::{jubjub::JubJubProjective as JubJub, ProjectiveCurve};
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
use r1cs_std::{
|
||||
|
groups::curves::twisted_edwards::jubjub::JubJubGadget,
|
||||
|
test_constraint_system::TestConstraintSystem, prelude::*,
|
||||
|
};
|
||||
|
|
||||
|
type TestCRH = PedersenCRH<JubJub, Window>;
|
||||
|
type TestCRHGadget = PedersenCRHGadget<JubJub, Fr, JubJubGadget>;
|
||||
|
|
||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
|
pub(super) struct Window;
|
||||
|
|
||||
|
impl PedersenWindow for Window {
|
||||
|
const WINDOW_SIZE: usize = 128;
|
||||
|
const NUM_WINDOWS: usize = 8;
|
||||
|
}
|
||||
|
|
||||
|
fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
|
||||
|
mut cs: CS,
|
||||
|
rng: &mut R,
|
||||
|
) -> ([u8; 128], Vec<UInt8>) {
|
||||
|
let mut input = [1u8; 128];
|
||||
|
rng.fill_bytes(&mut input);
|
||||
|
|
||||
|
let mut input_bytes = vec![];
|
||||
|
for (byte_i, input_byte) in input.into_iter().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
|
||||
|
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
|
||||
|
}
|
||||
|
(input, input_bytes)
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn crh_primitive_gadget_test() {
|
||||
|
let rng = &mut thread_rng();
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let (input, input_bytes) = generate_input(&mut cs, 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"),
|
||||
|
|| Ok(¶meters),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
println!(
|
||||
|
"number of constraints for input + params: {}",
|
||||
|
cs.num_constraints()
|
||||
|
);
|
||||
|
|
||||
|
let gadget_result =
|
||||
|
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
|
||||
|
&mut cs.ns(|| "gadget_evaluation"),
|
||||
|
&gadget_parameters,
|
||||
|
&input_bytes,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
println!("number of constraints total: {}", cs.num_constraints());
|
||||
|
|
||||
|
let primitive_result = primitive_result.into_affine();
|
||||
|
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
|
||||
|
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,141 @@ |
|||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
use rayon::prelude::*;
|
||||
|
use std::{
|
||||
|
fmt::{Debug, Formatter, Result as FmtResult},
|
||||
|
marker::PhantomData,
|
||||
|
};
|
||||
|
|
||||
|
use crate::crh::FixedLengthCRH;
|
||||
|
use algebra::groups::Group;
|
||||
|
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
pub trait PedersenWindow: Clone {
|
||||
|
const WINDOW_SIZE: usize;
|
||||
|
const NUM_WINDOWS: usize;
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Clone, Default)]
|
||||
|
pub struct PedersenParameters<G: Group> {
|
||||
|
pub generators: Vec<Vec<G>>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct PedersenCRH<G: Group, W: PedersenWindow> {
|
||||
|
group: PhantomData<G>,
|
||||
|
window: PhantomData<W>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, W: PedersenWindow> PedersenCRH<G, W> {
|
||||
|
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<G>> {
|
||||
|
let mut generators_powers = Vec::new();
|
||||
|
for _ in 0..W::NUM_WINDOWS {
|
||||
|
generators_powers.push(Self::generator_powers(W::WINDOW_SIZE, rng));
|
||||
|
}
|
||||
|
generators_powers
|
||||
|
}
|
||||
|
|
||||
|
pub fn generator_powers<R: Rng>(num_powers: usize, rng: &mut R) -> Vec<G> {
|
||||
|
let mut cur_gen_powers = Vec::with_capacity(num_powers);
|
||||
|
let mut base = G::rand(rng);
|
||||
|
for _ in 0..num_powers {
|
||||
|
cur_gen_powers.push(base);
|
||||
|
base.double_in_place();
|
||||
|
}
|
||||
|
cur_gen_powers
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, W: PedersenWindow> FixedLengthCRH for PedersenCRH<G, W> {
|
||||
|
const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS;
|
||||
|
type Output = G;
|
||||
|
type Parameters = PedersenParameters<G>;
|
||||
|
|
||||
|
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
||||
|
let time = start_timer!(|| format!(
|
||||
|
"PedersenCRH::Setup: {} {}-bit windows; {{0,1}}^{{{}}} -> G",
|
||||
|
W::NUM_WINDOWS,
|
||||
|
W::WINDOW_SIZE,
|
||||
|
W::NUM_WINDOWS * W::WINDOW_SIZE
|
||||
|
));
|
||||
|
let generators = Self::create_generators(rng);
|
||||
|
end_timer!(time);
|
||||
|
Ok(Self::Parameters { generators })
|
||||
|
}
|
||||
|
|
||||
|
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
|
||||
|
let eval_time = start_timer!(|| "PedersenCRH::Eval");
|
||||
|
|
||||
|
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
panic!(
|
||||
|
"incorrect input length {:?} for window params {:?}x{:?}",
|
||||
|
input.len(),
|
||||
|
W::WINDOW_SIZE,
|
||||
|
W::NUM_WINDOWS
|
||||
|
);
|
||||
|
}
|
||||
|
|
||||
|
let mut padded_input = Vec::with_capacity(input.len());
|
||||
|
let mut input = input;
|
||||
|
// Pad the input if it is not the current length.
|
||||
|
if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
|
||||
|
let current_length = input.len();
|
||||
|
padded_input.extend_from_slice(input);
|
||||
|
for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
|
||||
|
padded_input.push(0u8);
|
||||
|
}
|
||||
|
input = padded_input.as_slice();
|
||||
|
}
|
||||
|
|
||||
|
assert_eq!(
|
||||
|
parameters.generators.len(),
|
||||
|
W::NUM_WINDOWS,
|
||||
|
"Incorrect pp of size {:?}x{:?} for window params {:?}x{:?}",
|
||||
|
parameters.generators[0].len(),
|
||||
|
parameters.generators.len(),
|
||||
|
W::WINDOW_SIZE,
|
||||
|
W::NUM_WINDOWS
|
||||
|
);
|
||||
|
|
||||
|
// Compute sum of h_i^{m_i} for all i.
|
||||
|
let result = bytes_to_bits(input)
|
||||
|
.par_chunks(W::WINDOW_SIZE)
|
||||
|
.zip(¶meters.generators)
|
||||
|
.map(|(bits, generator_powers)| {
|
||||
|
let mut encoded = G::zero();
|
||||
|
for (bit, base) in bits.iter().zip(generator_powers.iter()) {
|
||||
|
if *bit {
|
||||
|
encoded = encoded + base;
|
||||
|
}
|
||||
|
}
|
||||
|
encoded
|
||||
|
})
|
||||
|
.reduce(|| G::zero(), |a, b| a + &b);
|
||||
|
end_timer!(eval_time);
|
||||
|
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
|
let mut bits = Vec::with_capacity(bytes.len() * 8);
|
||||
|
for byte in bytes {
|
||||
|
for i in 0..8 {
|
||||
|
let bit = (*byte >> i) & 1;
|
||||
|
bits.push(bit == 1)
|
||||
|
}
|
||||
|
}
|
||||
|
bits
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group> Debug for PedersenParameters<G> {
|
||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
|
write!(f, "Pedersen Hash Parameters {{\n")?;
|
||||
|
for (i, g) in self.generators.iter().enumerate() {
|
||||
|
write!(f, "\t Generator {}: {:?}\n", i, g)?;
|
||||
|
}
|
||||
|
write!(f, "}}\n")
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,46 @@ |
|||||
|
#[macro_use]
|
||||
|
extern crate bench_utils;
|
||||
|
|
||||
|
#[macro_use]
|
||||
|
extern crate derivative;
|
||||
|
|
||||
|
pub mod commitment;
|
||||
|
pub mod crh;
|
||||
|
pub mod mht;
|
||||
|
pub mod nizk;
|
||||
|
pub mod prf;
|
||||
|
pub mod signature;
|
||||
|
|
||||
|
pub use self::{
|
||||
|
commitment::CommitmentScheme,
|
||||
|
crh::FixedLengthCRH,
|
||||
|
mht::{HashMembershipProof, MerkleHashTree},
|
||||
|
nizk::NIZK,
|
||||
|
prf::PRF,
|
||||
|
signature::SignatureScheme,
|
||||
|
};
|
||||
|
|
||||
|
pub type Error = Box<dyn std::error::Error>;
|
||||
|
|
||||
|
#[derive(Debug)]
|
||||
|
pub enum CryptoError {
|
||||
|
IncorrectInputLength(usize),
|
||||
|
NotPrimeOrder,
|
||||
|
}
|
||||
|
|
||||
|
impl std::fmt::Display for CryptoError {
|
||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
let msg = match self {
|
||||
|
CryptoError::IncorrectInputLength(len) => format!("input length is wrong: {}", len),
|
||||
|
CryptoError::NotPrimeOrder => "element is not prime order".to_owned(),
|
||||
|
};
|
||||
|
write!(f, "{}", msg)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl std::error::Error for CryptoError {
|
||||
|
#[inline]
|
||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
|
None
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,512 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
use r1cs_std::boolean::AllocatedBit;
|
||||
|
|
||||
|
use crate::{mht::HashMembershipProof, CommitmentScheme, FixedLengthCRH};
|
||||
|
|
||||
|
use crate::{commitment::CommitmentGadget, crh::FixedLengthCRHGadget};
|
||||
|
|
||||
|
use crate::ledger::{CommPath, Digest, LedgerDigest, LedgerWitness};
|
||||
|
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
pub trait LCWGadget<C: CommitmentScheme, D: LedgerDigest, CW: LedgerWitness<D>, ConstraintF: Field> {
|
||||
|
type ParametersGadget: AllocGadget<D::Parameters, ConstraintF>;
|
||||
|
type CommitmentGadget: AllocGadget<C::Output, ConstraintF>;
|
||||
|
type DigestGadget: AllocGadget<D, ConstraintF>;
|
||||
|
type WitnessGadget: AllocGadget<CW, ConstraintF>;
|
||||
|
|
||||
|
fn check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
ledger_state_digest: &Self::DigestGadget,
|
||||
|
commitment: &Self::CommitmentGadget,
|
||||
|
witness: &Self::WitnessGadget,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
Self::conditionally_check_witness_gadget(
|
||||
|
cs,
|
||||
|
parameters,
|
||||
|
ledger_state_digest,
|
||||
|
commitment,
|
||||
|
witness,
|
||||
|
&Boolean::Constant(true),
|
||||
|
)
|
||||
|
}
|
||||
|
|
||||
|
fn conditionally_check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
ledger_state_digest: &Self::DigestGadget,
|
||||
|
commitment: &Self::CommitmentGadget,
|
||||
|
witness: &Self::WitnessGadget,
|
||||
|
should_enforce: &Boolean,
|
||||
|
) -> Result<(), SynthesisError>;
|
||||
|
}
|
||||
|
|
||||
|
pub struct IdealLedgerGadget<C, H, HGadget, CGadget> {
|
||||
|
#[doc(hidden)]
|
||||
|
_comm_scheme: PhantomData<C>,
|
||||
|
#[doc(hidden)]
|
||||
|
_hash: PhantomData<H>,
|
||||
|
#[doc(hidden)]
|
||||
|
_comm_gadget: PhantomData<CGadget>,
|
||||
|
#[doc(hidden)]
|
||||
|
_hash_gadget: PhantomData<HGadget>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct CommitmentWitness<
|
||||
|
H: FixedLengthCRH,
|
||||
|
C: CommitmentScheme,
|
||||
|
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
|
||||
|
ConstraintF: Field,
|
||||
|
> {
|
||||
|
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
|
||||
|
_crh: PhantomData<H>,
|
||||
|
_comm: PhantomData<C>,
|
||||
|
_engine: PhantomData<ConstraintF>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct DigestGadget<H: FixedLengthCRH, HGadget: FixedLengthCRHGadget<H, ConstraintF>, ConstraintF: Field> {
|
||||
|
digest: HGadget::OutputGadget,
|
||||
|
#[doc(hidden)]
|
||||
|
_crh: PhantomData<H>,
|
||||
|
#[doc(hidden)]
|
||||
|
_engine: PhantomData<ConstraintF>,
|
||||
|
}
|
||||
|
|
||||
|
impl<C, ConstraintF, H, CGadget, HGadget> LCWGadget<C, Digest<H>, CommPath<H, C::Output>, ConstraintF>
|
||||
|
for IdealLedgerGadget<C, H, HGadget, CGadget>
|
||||
|
where
|
||||
|
C: CommitmentScheme,
|
||||
|
C::Output: Eq,
|
||||
|
ConstraintF: Field,
|
||||
|
H: FixedLengthCRH,
|
||||
|
CGadget: CommitmentGadget<C, ConstraintF>,
|
||||
|
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
|
||||
|
{
|
||||
|
type ParametersGadget = <HGadget as FixedLengthCRHGadget<H, ConstraintF>>::ParametersGadget;
|
||||
|
type DigestGadget = DigestGadget<H, HGadget, ConstraintF>;
|
||||
|
|
||||
|
type CommitmentGadget = <CGadget as CommitmentGadget<C, ConstraintF>>::OutputGadget;
|
||||
|
type WitnessGadget = CommitmentWitness<H, C, HGadget, ConstraintF>;
|
||||
|
|
||||
|
/// Given a `leaf` and `path`, check that the `path` is a valid
|
||||
|
/// authentication path for the `leaf` in a Merkle tree.
|
||||
|
/// Note: It is assumed that the root is contained in the `path`.
|
||||
|
fn conditionally_check_witness_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
root_hash: &Self::DigestGadget,
|
||||
|
commitment: &Self::CommitmentGadget,
|
||||
|
witness: &Self::WitnessGadget,
|
||||
|
should_enforce: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
assert_eq!(
|
||||
|
witness.path.len(),
|
||||
|
(HashMembershipProof::<H, C::Output>::MAX_HEIGHT - 1) as usize
|
||||
|
);
|
||||
|
// Check that the hash of the given leaf matches the leaf hash in the membership
|
||||
|
// proof.
|
||||
|
let commitment_bits = commitment.to_bytes(&mut cs.ns(|| "commitment_to_bytes"))?;
|
||||
|
let commitment_hash = HGadget::check_evaluation_gadget(
|
||||
|
cs.ns(|| "check_evaluation_gadget"),
|
||||
|
parameters,
|
||||
|
&commitment_bits,
|
||||
|
)?;
|
||||
|
|
||||
|
// Check if leaf is one of the bottom-most siblings.
|
||||
|
let leaf_is_left = AllocatedBit::alloc(&mut cs.ns(|| "leaf_is_left"), || {
|
||||
|
Ok(commitment_hash == witness.path[0].0)
|
||||
|
})?
|
||||
|
.into();
|
||||
|
<HGadget::OutputGadget>::conditional_enforce_equal_or(
|
||||
|
&mut cs.ns(|| "check_leaf_is_left"),
|
||||
|
&leaf_is_left,
|
||||
|
&commitment_hash,
|
||||
|
&witness.path[0].0,
|
||||
|
&witness.path[0].1,
|
||||
|
should_enforce,
|
||||
|
)?;
|
||||
|
|
||||
|
// Check levels between leaf level and root.
|
||||
|
let mut previous_hash = commitment_hash;
|
||||
|
for (i, &(ref left_hash, ref right_hash)) in witness.path.iter().enumerate() {
|
||||
|
// 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();
|
||||
|
|
||||
|
<HGadget::OutputGadget>::conditional_enforce_equal_or(
|
||||
|
&mut cs.ns(|| format!("check_equals_which_{}", i)),
|
||||
|
&previous_is_left,
|
||||
|
&previous_hash,
|
||||
|
left_hash,
|
||||
|
right_hash,
|
||||
|
should_enforce,
|
||||
|
)?;
|
||||
|
|
||||
|
previous_hash = hash_inner_node_gadget::<H, HGadget, ConstraintF, _>(
|
||||
|
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
|
||||
|
parameters,
|
||||
|
left_hash,
|
||||
|
right_hash,
|
||||
|
)?;
|
||||
|
}
|
||||
|
|
||||
|
root_hash.digest.conditional_enforce_equal(
|
||||
|
&mut cs.ns(|| "root_is_last"),
|
||||
|
&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>
|
||||
|
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 mut bytes = left_bytes;
|
||||
|
bytes.extend_from_slice(&right_bytes);
|
||||
|
|
||||
|
HG::check_evaluation_gadget(cs, parameters, &bytes)
|
||||
|
}
|
||||
|
|
||||
|
impl<H, HGadget, ConstraintF> AllocGadget<Digest<H>, ConstraintF> for DigestGadget<H, HGadget, ConstraintF>
|
||||
|
where
|
||||
|
H: FixedLengthCRH,
|
||||
|
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
|
||||
|
ConstraintF: Field,
|
||||
|
{
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Digest<H>>,
|
||||
|
{
|
||||
|
let digest = HGadget::OutputGadget::alloc(&mut cs.ns(|| "digest"), || {
|
||||
|
Ok(value_gen()?.borrow().0.clone())
|
||||
|
})?;
|
||||
|
|
||||
|
Ok(DigestGadget {
|
||||
|
digest,
|
||||
|
_crh: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
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<Digest<H>>,
|
||||
|
{
|
||||
|
let digest = HGadget::OutputGadget::alloc_input(&mut cs.ns(|| "input_digest"), || {
|
||||
|
Ok(value_gen()?.borrow().0.clone())
|
||||
|
})?;
|
||||
|
Ok(DigestGadget {
|
||||
|
digest,
|
||||
|
_crh: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<H, C, HGadget, ConstraintF> AllocGadget<CommPath<H, C::Output>, ConstraintF>
|
||||
|
for CommitmentWitness<H, C, HGadget, ConstraintF>
|
||||
|
where
|
||||
|
H: FixedLengthCRH,
|
||||
|
C: CommitmentScheme,
|
||||
|
HGadget: FixedLengthCRHGadget<H, ConstraintF>,
|
||||
|
ConstraintF: Field,
|
||||
|
{
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<CommPath<H, C::Output>>,
|
||||
|
{
|
||||
|
let mut path = Vec::new();
|
||||
|
for (i, &(ref l, ref r)) in value_gen()?.borrow().0.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(CommitmentWitness {
|
||||
|
path,
|
||||
|
_crh: PhantomData,
|
||||
|
_comm: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
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<CommPath<H, C::Output>>,
|
||||
|
{
|
||||
|
let mut path = Vec::new();
|
||||
|
for (i, &(ref l, ref r)) in value_gen()?.borrow().0.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(CommitmentWitness {
|
||||
|
path,
|
||||
|
_crh: PhantomData,
|
||||
|
_comm: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
use crate::crypto_primitives::{
|
||||
|
commitment::{
|
||||
|
pedersen::{PedersenCommitment, PedersenRandomness},
|
||||
|
CommitmentScheme,
|
||||
|
},
|
||||
|
crh::{
|
||||
|
pedersen::{PedersenCRH, PedersenWindow},
|
||||
|
FixedLengthCRH,
|
||||
|
},
|
||||
|
mht::*,
|
||||
|
};
|
||||
|
use algebra::{
|
||||
|
curves::jubjub::JubJubAffine as JubJub,
|
||||
|
fields::jubjub::fr::Fr,
|
||||
|
fields::jubjub::fq::Fq,
|
||||
|
Group
|
||||
|
};
|
||||
|
use rand::SeedableRng;
|
||||
|
use algebra::UniformRand;
|
||||
|
use rand_xorshift::XorShiftRng;
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
use super::*;
|
||||
|
use crate::gadgets::{
|
||||
|
commitment::pedersen::PedersenCommitmentGadget,
|
||||
|
crh::{pedersen::PedersenCRHGadget, FixedLengthCRHGadget},
|
||||
|
};
|
||||
|
use r1cs_std::{
|
||||
|
groups::curves::twisted_edwards::jubjub::JubJubGadget,
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
|
||||
|
use crate::ledger::{CommPath, Digest};
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub(super) struct Window4x256;
|
||||
|
impl PedersenWindow for Window4x256 {
|
||||
|
const WINDOW_SIZE: usize = 4;
|
||||
|
const NUM_WINDOWS: usize = 256;
|
||||
|
}
|
||||
|
|
||||
|
type H = PedersenCRH<JubJub, Window4x256>;
|
||||
|
type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
|
||||
|
type C = PedersenCommitment<JubJub, Window4x256>;
|
||||
|
type CG = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
|
||||
|
type JubJubMHT = MerkleHashTree<H, <C as CommitmentScheme>::Output>;
|
||||
|
type LG = IdealLedgerGadget<C, H, HG, CG>;
|
||||
|
type DG = DigestGadget<H, HG, Fq>;
|
||||
|
type LCWG = CommitmentWitness<H, C, HG, Fq>;
|
||||
|
|
||||
|
fn generate_merkle_tree(leaves: &[<C as CommitmentScheme>::Output]) -> () {
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
|
||||
|
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
|
||||
|
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
|
||||
|
let root = tree.root();
|
||||
|
let mut satisfied = true;
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
let proof = tree.generate_proof(i, &leaf).unwrap();
|
||||
|
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
|
||||
|
let digest = DG::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
|
||||
|
Ok(Digest(root))
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
let constraints_from_digest = cs.num_constraints();
|
||||
|
println!("constraints from digest: {}", constraints_from_digest);
|
||||
|
|
||||
|
let crh_parameters =
|
||||
|
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
|
||||
|
&mut cs.ns(|| format!("new_parameters_{}", i)),
|
||||
|
|| Ok(crh_parameters.clone()),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
|
||||
|
println!(
|
||||
|
"constraints from parameters: {}",
|
||||
|
constraints_from_parameters
|
||||
|
);
|
||||
|
|
||||
|
let comm = <CG as CommitmentGadget<C, Fq>>::OutputGadget::alloc(
|
||||
|
&mut cs.ns(|| format!("new_comm_{}", i)),
|
||||
|
|| {
|
||||
|
let leaf: JubJub = *leaf;
|
||||
|
Ok(leaf)
|
||||
|
},
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let constraints_from_comm =
|
||||
|
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
|
||||
|
println!("constraints from comm: {}", constraints_from_comm);
|
||||
|
|
||||
|
let cw = LCWG::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
|
||||
|
Ok(CommPath(proof))
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
let constraints_from_path = cs.num_constraints()
|
||||
|
- constraints_from_parameters
|
||||
|
- constraints_from_digest
|
||||
|
- constraints_from_comm;
|
||||
|
println!("constraints from path: {}", constraints_from_path);
|
||||
|
LG::check_witness_gadget(
|
||||
|
&mut cs.ns(|| format!("new_witness_check_{}", i)),
|
||||
|
&crh_parameters,
|
||||
|
&digest,
|
||||
|
&comm,
|
||||
|
&cw,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
if !cs.is_satisfied() {
|
||||
|
satisfied = false;
|
||||
|
println!(
|
||||
|
"Unsatisfied constraint: {}",
|
||||
|
cs.which_is_unsatisfied().unwrap()
|
||||
|
);
|
||||
|
}
|
||||
|
let setup_constraints = constraints_from_comm
|
||||
|
+ constraints_from_digest
|
||||
|
+ constraints_from_parameters
|
||||
|
+ constraints_from_path;
|
||||
|
println!(
|
||||
|
"number of constraints: {}",
|
||||
|
cs.num_constraints() - setup_constraints
|
||||
|
);
|
||||
|
}
|
||||
|
assert!(satisfied);
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn mht_gadget_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
let comm_parameters = C::setup(&mut rng).unwrap();
|
||||
|
for i in 0..4u8 {
|
||||
|
let r = PedersenRandomness(Fr::rand(&mut rng));
|
||||
|
let input = [i, i, i, i, i, i, i, i];
|
||||
|
leaves.push(C::commit(&comm_parameters, &input, &r).unwrap());
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves);
|
||||
|
// let mut leaves = Vec::new();
|
||||
|
// for i in 0..100u8 {
|
||||
|
// leaves.push([i, i, i, i, i, i, i, i]);
|
||||
|
// }
|
||||
|
// generate_merkle_tree(&leaves);
|
||||
|
}
|
||||
|
|
||||
|
fn bad_merkle_tree_verify(leaves: &[<C as CommitmentScheme>::Output]) -> () {
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
|
||||
|
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
|
||||
|
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
|
||||
|
let root = tree.root();
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
let proof = tree.generate_proof(i, &leaf).unwrap();
|
||||
|
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
|
||||
|
let digest = DG::alloc(&mut cs.ns(|| format!("new_digest_{}", i)), || {
|
||||
|
Ok(Digest(JubJub::zero()))
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
let crh_parameters =
|
||||
|
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
|
||||
|
&mut cs.ns(|| format!("new_parameters_{}", i)),
|
||||
|
|| Ok(crh_parameters.clone()),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let comm = <CG as CommitmentGadget<C, Fq>>::OutputGadget::alloc(
|
||||
|
&mut cs.ns(|| format!("new_comm_{}", i)),
|
||||
|
|| {
|
||||
|
let leaf = *leaf;
|
||||
|
Ok(leaf)
|
||||
|
},
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
let cw = LCWG::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
|
||||
|
Ok(CommPath(proof))
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
LG::check_witness_gadget(
|
||||
|
&mut cs.ns(|| format!("new_witness_check_{}", i)),
|
||||
|
&crh_parameters,
|
||||
|
&digest,
|
||||
|
&comm,
|
||||
|
&cw,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!(
|
||||
|
"Unsatisfied constraints: {}",
|
||||
|
cs.which_is_unsatisfied().unwrap()
|
||||
|
);
|
||||
|
}
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[should_panic]
|
||||
|
#[test]
|
||||
|
fn bad_root_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
let comm_parameters = C::setup(&mut rng).unwrap();
|
||||
|
for i in 0..4u8 {
|
||||
|
let r = PedersenRandomness(Fr::rand(&mut rng));
|
||||
|
let input = [i, i, i, i, i, i, i, i];
|
||||
|
leaves.push(C::commit(&comm_parameters, &input, &r).unwrap());
|
||||
|
}
|
||||
|
bad_merkle_tree_verify(&leaves);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,353 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
use r1cs_std::boolean::AllocatedBit;
|
||||
|
|
||||
|
use crate::mht::*;
|
||||
|
use crate::crh::{FixedLengthCRH, FixedLengthCRHGadget};
|
||||
|
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
pub struct MerklePath<P, HGadget, ConstraintF>
|
||||
|
where
|
||||
|
P: MHTParameters,
|
||||
|
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
|
ConstraintF: Field,
|
||||
|
{
|
||||
|
path: Vec<(HGadget::OutputGadget, HGadget::OutputGadget)>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct MerklePathVerifierGadget<P, CRHGadget, ConstraintF>
|
||||
|
where
|
||||
|
P: MHTParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
|
{
|
||||
|
_p: PhantomData<CRHGadget>,
|
||||
|
_p2: PhantomData<P>,
|
||||
|
_f: PhantomData<ConstraintF>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P, CRHGadget, ConstraintF> MerklePathVerifierGadget<P, CRHGadget, ConstraintF>
|
||||
|
where
|
||||
|
P: MHTParameters,
|
||||
|
ConstraintF: Field,
|
||||
|
CRHGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
|
{
|
||||
|
|
||||
|
pub fn check_membership<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
parameters: &CRHGadget::ParametersGadget,
|
||||
|
root: &CRHGadget::OutputGadget,
|
||||
|
leaf: impl ToBytesGadget<ConstraintF>,
|
||||
|
witness: &MerklePath<P, CRHGadget, ConstraintF>,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
Self::conditionally_check_membership(
|
||||
|
cs,
|
||||
|
parameters,
|
||||
|
root,
|
||||
|
leaf,
|
||||
|
witness,
|
||||
|
&Boolean::Constant(true),
|
||||
|
)
|
||||
|
}
|
||||
|
|
||||
|
pub fn conditionally_check_membership<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
parameters: &CRHGadget::ParametersGadget,
|
||||
|
root: &CRHGadget::OutputGadget,
|
||||
|
leaf: impl ToBytesGadget<ConstraintF>,
|
||||
|
witness: &MerklePath<P, CRHGadget, ConstraintF>,
|
||||
|
should_enforce: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
assert_eq!(witness.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,
|
||||
|
)?;
|
||||
|
|
||||
|
// 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 == witness.path[0].0)
|
||||
|
})?
|
||||
|
.into();
|
||||
|
CRHGadget::OutputGadget::conditional_enforce_equal_or(
|
||||
|
&mut cs.ns(|| "check_leaf_is_left"),
|
||||
|
&leaf_is_left,
|
||||
|
&leaf_hash,
|
||||
|
&witness.path[0].0,
|
||||
|
&witness.path[0].1,
|
||||
|
should_enforce,
|
||||
|
)?;
|
||||
|
|
||||
|
// Check levels between leaf level and root.
|
||||
|
let mut previous_hash = leaf_hash;
|
||||
|
for (i, &(ref left_hash, ref right_hash)) in witness.path.iter().enumerate() {
|
||||
|
// Check if the previous_hash matches the correct current hash.
|
||||
|
let previous_is_left =
|
||||
|
AllocatedBit::alloc(&mut cs.ns(|| format!("previous_is_left_{}", i)), || {
|
||||
|
Ok(&previous_hash == left_hash)
|
||||
|
})?
|
||||
|
.into();
|
||||
|
|
||||
|
CRHGadget::OutputGadget::conditional_enforce_equal_or(
|
||||
|
&mut cs.ns(|| format!("check_equals_which_{}", i)),
|
||||
|
&previous_is_left,
|
||||
|
&previous_hash,
|
||||
|
left_hash,
|
||||
|
right_hash,
|
||||
|
should_enforce,
|
||||
|
)?;
|
||||
|
|
||||
|
previous_hash = hash_inner_node_gadget::<P::H, CRHGadget, ConstraintF, _>(
|
||||
|
&mut cs.ns(|| format!("hash_inner_node_{}", i)),
|
||||
|
parameters,
|
||||
|
left_hash,
|
||||
|
right_hash,
|
||||
|
)?;
|
||||
|
}
|
||||
|
|
||||
|
root.conditional_enforce_equal(
|
||||
|
&mut cs.ns(|| "root_is_last"),
|
||||
|
&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>
|
||||
|
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 mut bytes = left_bytes;
|
||||
|
bytes.extend_from_slice(&right_bytes);
|
||||
|
|
||||
|
HG::check_evaluation_gadget(cs, parameters, &bytes)
|
||||
|
}
|
||||
|
|
||||
|
impl<P, HGadget, ConstraintF> AllocGadget<HashMembershipProof<P>, ConstraintF>
|
||||
|
for MerklePath<P, HGadget, ConstraintF>
|
||||
|
where
|
||||
|
P: MHTParameters,
|
||||
|
HGadget: FixedLengthCRHGadget<P::H, ConstraintF>,
|
||||
|
ConstraintF: Field,
|
||||
|
{
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: F,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<HashMembershipProof<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(MerklePath {
|
||||
|
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<HashMembershipProof<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(MerklePath {
|
||||
|
path,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
use crate::{
|
||||
|
crh::{
|
||||
|
pedersen::{PedersenCRH, PedersenWindow},
|
||||
|
pedersen::constraints::PedersenCRHGadget,
|
||||
|
FixedLengthCRH,
|
||||
|
FixedLengthCRHGadget,
|
||||
|
},
|
||||
|
mht::*,
|
||||
|
};
|
||||
|
use algebra::{
|
||||
|
curves::jubjub::JubJubAffine as JubJub,
|
||||
|
fields::jubjub::fq::Fq,
|
||||
|
};
|
||||
|
use rand::SeedableRng;
|
||||
|
use rand_xorshift::XorShiftRng;
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
use super::*;
|
||||
|
use r1cs_std::{
|
||||
|
groups::curves::twisted_edwards::jubjub::JubJubGadget,
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub(super) struct Window4x256;
|
||||
|
impl PedersenWindow for Window4x256 {
|
||||
|
const WINDOW_SIZE: usize = 4;
|
||||
|
const NUM_WINDOWS: usize = 256;
|
||||
|
}
|
||||
|
|
||||
|
type H = PedersenCRH<JubJub, Window4x256>;
|
||||
|
type HG = PedersenCRHGadget<JubJub, Fq, JubJubGadget>;
|
||||
|
|
||||
|
struct JubJubMHTParams;
|
||||
|
|
||||
|
impl MHTParameters for JubJubMHTParams {
|
||||
|
const HEIGHT: usize = 32;
|
||||
|
type H = H;
|
||||
|
}
|
||||
|
|
||||
|
type JubJubMHT = MerkleHashTree<JubJubMHTParams>;
|
||||
|
|
||||
|
fn generate_merkle_tree(leaves: &[[u8; 30]], use_bad_root: bool) -> () {
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
||||
|
|
||||
|
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
|
||||
|
let tree = JubJubMHT::new(crh_parameters.clone(), leaves).unwrap();
|
||||
|
let root = tree.root();
|
||||
|
let mut satisfied = true;
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
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)), || {
|
||||
|
if use_bad_root {
|
||||
|
Ok(<H as FixedLengthCRH>::Output::default())
|
||||
|
} else {
|
||||
|
Ok(root)
|
||||
|
}
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
|
||||
|
let constraints_from_digest = cs.num_constraints();
|
||||
|
println!("constraints from digest: {}", constraints_from_digest);
|
||||
|
|
||||
|
// Allocate Parameters for CRH
|
||||
|
let crh_parameters =
|
||||
|
<HG as FixedLengthCRHGadget<H, Fq>>::ParametersGadget::alloc(
|
||||
|
&mut cs.ns(|| format!("new_parameters_{}", i)),
|
||||
|
|| Ok(crh_parameters.clone()),
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
let constraints_from_parameters = cs.num_constraints() - constraints_from_digest;
|
||||
|
println!(
|
||||
|
"constraints from parameters: {}",
|
||||
|
constraints_from_parameters
|
||||
|
);
|
||||
|
|
||||
|
// Allocate Leaf
|
||||
|
let leaf_g = UInt8::constant_vec(leaf);
|
||||
|
|
||||
|
let constraints_from_leaf =
|
||||
|
cs.num_constraints() - constraints_from_parameters - constraints_from_digest;
|
||||
|
println!("constraints from leaf: {}", constraints_from_leaf);
|
||||
|
|
||||
|
// Allocate Merkle Tree Path
|
||||
|
let cw = MerklePath::<_, HG, _>::alloc(&mut cs.ns(|| format!("new_witness_{}", i)), || {
|
||||
|
Ok(proof)
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
|
||||
|
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();
|
||||
|
MerklePathVerifierGadget::<_, HG, _>::check_membership(
|
||||
|
&mut cs.ns(|| format!("new_witness_check_{}", i)),
|
||||
|
&crh_parameters,
|
||||
|
&root,
|
||||
|
&leaf_g,
|
||||
|
&cw,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
if !cs.is_satisfied() {
|
||||
|
satisfied = false;
|
||||
|
println!(
|
||||
|
"Unsatisfied constraint: {}",
|
||||
|
cs.which_is_unsatisfied().unwrap()
|
||||
|
);
|
||||
|
}
|
||||
|
let setup_constraints = constraints_from_leaf
|
||||
|
+ constraints_from_digest
|
||||
|
+ constraints_from_parameters
|
||||
|
+ constraints_from_path;
|
||||
|
println!(
|
||||
|
"number of constraints: {}",
|
||||
|
cs.num_constraints() - setup_constraints
|
||||
|
);
|
||||
|
}
|
||||
|
|
||||
|
assert!(satisfied);
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn mht_gadget_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..4u8 {
|
||||
|
let input = [i ; 30];
|
||||
|
leaves.push(input);
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves, false);
|
||||
|
}
|
||||
|
|
||||
|
#[should_panic]
|
||||
|
#[test]
|
||||
|
fn bad_root_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..4u8 {
|
||||
|
let input = [i ; 30];
|
||||
|
leaves.push(input);
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves, true);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,437 @@ |
|||||
|
use crate::crh::FixedLengthCRH;
|
||||
|
use algebra::bytes::ToBytes;
|
||||
|
use crate::Error;
|
||||
|
use std::{fmt, rc::Rc};
|
||||
|
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
pub trait MHTParameters {
|
||||
|
const HEIGHT: usize;
|
||||
|
type H: FixedLengthCRH;
|
||||
|
}
|
||||
|
|
||||
|
/// Stores the hashes of a particular path (in order) from leaf to root.
|
||||
|
/// Our path `is_left_child()` if the boolean in `path` is true.
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(
|
||||
|
Clone(bound = "P: MHTParameters"),
|
||||
|
Debug(bound = "P: MHTParameters, <P::H as FixedLengthCRH>::Output: fmt::Debug")
|
||||
|
)]
|
||||
|
pub struct HashMembershipProof<P: MHTParameters> {
|
||||
|
pub(crate) path: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P: MHTParameters> Default for HashMembershipProof<P> {
|
||||
|
fn default() -> Self {
|
||||
|
let mut path = Vec::with_capacity(P::HEIGHT as usize);
|
||||
|
for _i in 1..P::HEIGHT as usize {
|
||||
|
path.push((<P::H as FixedLengthCRH>::Output::default(), <P::H as FixedLengthCRH>::Output::default()));
|
||||
|
}
|
||||
|
Self {
|
||||
|
path,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<P: MHTParameters> HashMembershipProof<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> {
|
||||
|
if self.path.len() != (P::HEIGHT - 1) as usize {
|
||||
|
return Ok(false);
|
||||
|
}
|
||||
|
// Check that the given leaf matches the leaf in the membership proof.
|
||||
|
let mut buffer = [0u8; 128];
|
||||
|
|
||||
|
if !self.path.is_empty() {
|
||||
|
let claimed_leaf_hash = hash_leaf::<P::H, L>(parameters, leaf, &mut buffer)?;
|
||||
|
|
||||
|
// Check if leaf is one of the bottom-most siblings.
|
||||
|
if claimed_leaf_hash != self.path[0].0 && claimed_leaf_hash != self.path[0].1 {
|
||||
|
return Ok(false);
|
||||
|
};
|
||||
|
|
||||
|
let mut prev = claimed_leaf_hash;
|
||||
|
// Check levels between leaf level and root.
|
||||
|
for &(ref hash, ref sibling_hash) in &self.path {
|
||||
|
// Check if the previous hash matches the correct current hash.
|
||||
|
if &prev != hash && &prev != sibling_hash {
|
||||
|
return Ok(false);
|
||||
|
};
|
||||
|
prev = hash_inner_node::<P::H>(parameters, hash, sibling_hash, &mut buffer)?;
|
||||
|
}
|
||||
|
|
||||
|
if root_hash != &prev {
|
||||
|
return Ok(false);
|
||||
|
}
|
||||
|
Ok(true)
|
||||
|
} else {
|
||||
|
Ok(false)
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub struct MerkleHashTree<P: MHTParameters> {
|
||||
|
tree: Vec<<P::H as FixedLengthCRH>::Output>,
|
||||
|
padding_tree: Vec<(<P::H as FixedLengthCRH>::Output, <P::H as FixedLengthCRH>::Output)>,
|
||||
|
parameters: Rc<<P::H as FixedLengthCRH>::Parameters>,
|
||||
|
root: Option<<P::H as FixedLengthCRH>::Output>,
|
||||
|
}
|
||||
|
|
||||
|
impl<P: MHTParameters> MerkleHashTree<P> {
|
||||
|
pub const HEIGHT: u8 = P::HEIGHT as u8;
|
||||
|
|
||||
|
pub fn blank(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>) -> Self {
|
||||
|
MerkleHashTree {
|
||||
|
tree: Vec::new(),
|
||||
|
padding_tree: Vec::new(),
|
||||
|
root: None,
|
||||
|
parameters,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn new<L: ToBytes>(parameters: Rc<<P::H as FixedLengthCRH>::Parameters>, leaves: &[L]) -> Result<Self, Error> {
|
||||
|
let new_time = start_timer!(|| "MHT::New");
|
||||
|
|
||||
|
let last_level_size = leaves.len().next_power_of_two();
|
||||
|
let tree_size = 2 * last_level_size - 1;
|
||||
|
let tree_height = tree_height(tree_size);
|
||||
|
assert!(tree_height as u8 <= Self::HEIGHT);
|
||||
|
|
||||
|
// Initialize the merkle tree.
|
||||
|
let mut tree = Vec::with_capacity(tree_size);
|
||||
|
let empty_hash = hash_empty::<P::H>(¶meters)?;
|
||||
|
for _ in 0..tree_size {
|
||||
|
tree.push(empty_hash.clone());
|
||||
|
}
|
||||
|
|
||||
|
// Compute the starting indices for each level of the tree.
|
||||
|
let mut index = 0;
|
||||
|
let mut level_indices = Vec::with_capacity(tree_height);
|
||||
|
for _ in 0..tree_height {
|
||||
|
level_indices.push(index);
|
||||
|
index = left_child(index);
|
||||
|
}
|
||||
|
|
||||
|
// Compute and store the hash values for each leaf.
|
||||
|
let last_level_index = level_indices.pop().unwrap();
|
||||
|
let mut buffer = [0u8; 128];
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
tree[last_level_index + i] = hash_leaf::<P::H, _>(¶meters, leaf, &mut buffer)?;
|
||||
|
}
|
||||
|
|
||||
|
// Compute the hash values for every node in the tree.
|
||||
|
let mut upper_bound = last_level_index;
|
||||
|
let mut buffer = [0u8; 128];
|
||||
|
level_indices.reverse();
|
||||
|
for &start_index in &level_indices {
|
||||
|
// Iterate over the current level.
|
||||
|
for current_index in start_index..upper_bound {
|
||||
|
let left_index = left_child(current_index);
|
||||
|
let right_index = right_child(current_index);
|
||||
|
|
||||
|
// Compute Hash(left || right).
|
||||
|
tree[current_index] = hash_inner_node::<P::H>(
|
||||
|
¶meters,
|
||||
|
&tree[left_index],
|
||||
|
&tree[right_index],
|
||||
|
&mut buffer,
|
||||
|
)?;
|
||||
|
}
|
||||
|
upper_bound = start_index;
|
||||
|
}
|
||||
|
// Finished computing actual tree.
|
||||
|
// Now, we compute the dummy nodes until we hit our HEIGHT goal.
|
||||
|
let mut cur_height = tree_height;
|
||||
|
let mut padding_tree = Vec::new();
|
||||
|
let mut cur_hash = tree[0].clone();
|
||||
|
while cur_height < (Self::HEIGHT - 1) as usize {
|
||||
|
cur_hash = hash_inner_node::<P::H>(¶meters, &cur_hash, &empty_hash, &mut buffer)?;
|
||||
|
padding_tree.push((cur_hash.clone(), empty_hash.clone()));
|
||||
|
cur_height += 1;
|
||||
|
}
|
||||
|
|
||||
|
let root_hash = hash_inner_node::<P::H>(¶meters, &cur_hash, &empty_hash, &mut buffer)?;
|
||||
|
|
||||
|
end_timer!(new_time);
|
||||
|
|
||||
|
Ok(MerkleHashTree {
|
||||
|
tree,
|
||||
|
padding_tree,
|
||||
|
parameters,
|
||||
|
root: Some(root_hash),
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn root(&self) -> <P::H as FixedLengthCRH>::Output {
|
||||
|
self.root.clone().unwrap()
|
||||
|
}
|
||||
|
|
||||
|
pub fn generate_proof<L: ToBytes>(
|
||||
|
&self,
|
||||
|
index: usize,
|
||||
|
leaf: &L,
|
||||
|
) -> Result<HashMembershipProof<P>, Error> {
|
||||
|
let prove_time = start_timer!(|| "MHT::GenProof");
|
||||
|
let mut path = Vec::new();
|
||||
|
|
||||
|
let mut buffer = [0u8; 128];
|
||||
|
let leaf_hash = hash_leaf::<P::H, _>(&self.parameters, leaf, &mut buffer)?;
|
||||
|
let tree_height = tree_height(self.tree.len());
|
||||
|
let tree_index = convert_index_to_last_level(index, tree_height);
|
||||
|
let empty_hash = hash_empty::<P::H>(&self.parameters)?;
|
||||
|
|
||||
|
// Check that the given index corresponds to the correct leaf.
|
||||
|
if leaf_hash != self.tree[tree_index] {
|
||||
|
Err(MHTError::IncorrectLeafIndex(tree_index))?
|
||||
|
}
|
||||
|
|
||||
|
// Iterate from the leaf up to the root, storing all intermediate hash values.
|
||||
|
let mut current_node = tree_index;
|
||||
|
while !is_root(current_node) {
|
||||
|
let sibling_node = sibling(current_node).unwrap();
|
||||
|
let (curr_hash, sibling_hash) = (
|
||||
|
self.tree[current_node].clone(),
|
||||
|
self.tree[sibling_node].clone(),
|
||||
|
);
|
||||
|
if is_left_child(current_node) {
|
||||
|
path.push((curr_hash, sibling_hash));
|
||||
|
} else {
|
||||
|
path.push((sibling_hash, curr_hash));
|
||||
|
}
|
||||
|
current_node = parent(current_node).unwrap();
|
||||
|
}
|
||||
|
|
||||
|
// Store the root node. Set boolean as true for consistency with digest
|
||||
|
// location.
|
||||
|
assert!(path.len() < Self::HEIGHT as usize);
|
||||
|
if path.len() != (Self::HEIGHT - 1) as usize {
|
||||
|
path.push((self.tree[0].clone(), empty_hash));
|
||||
|
for &(ref hash, ref sibling_hash) in &self.padding_tree {
|
||||
|
path.push((hash.clone(), sibling_hash.clone()));
|
||||
|
}
|
||||
|
}
|
||||
|
end_timer!(prove_time);
|
||||
|
if path.len() != (Self::HEIGHT - 1) as usize {
|
||||
|
Err(MHTError::IncorrectPathLength(path.len()))?
|
||||
|
} else {
|
||||
|
Ok(HashMembershipProof {
|
||||
|
path,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug)]
|
||||
|
pub enum MHTError {
|
||||
|
IncorrectLeafIndex(usize),
|
||||
|
IncorrectPathLength(usize),
|
||||
|
}
|
||||
|
|
||||
|
impl std::fmt::Display for MHTError {
|
||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
let msg = match self {
|
||||
|
MHTError::IncorrectLeafIndex(index) => format!("incorrect leaf index: {}", index),
|
||||
|
MHTError::IncorrectPathLength(len) => format!("incorrect path length: {}", len),
|
||||
|
};
|
||||
|
write!(f, "{}", msg)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl std::error::Error for MHTError {
|
||||
|
#[inline]
|
||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
|
None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the log2 value of the given number.
|
||||
|
#[inline]
|
||||
|
fn log2(number: usize) -> usize {
|
||||
|
(number as f64).log2() as usize
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the height of the tree, given the size of the tree.
|
||||
|
#[inline]
|
||||
|
fn tree_height(tree_size: usize) -> usize {
|
||||
|
log2(tree_size + 1)
|
||||
|
}
|
||||
|
|
||||
|
/// Returns true iff the index represents the root.
|
||||
|
#[inline]
|
||||
|
fn is_root(index: usize) -> bool {
|
||||
|
index == 0
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the index of the left child, given an index.
|
||||
|
#[inline]
|
||||
|
fn left_child(index: usize) -> usize {
|
||||
|
2 * index + 1
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the index of the right child, given an index.
|
||||
|
#[inline]
|
||||
|
fn right_child(index: usize) -> usize {
|
||||
|
2 * index + 2
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the index of the sibling, given an index.
|
||||
|
#[inline]
|
||||
|
fn sibling(index: usize) -> Option<usize> {
|
||||
|
if index == 0 {
|
||||
|
None
|
||||
|
} else if is_left_child(index) {
|
||||
|
Some(index + 1)
|
||||
|
} else {
|
||||
|
Some(index - 1)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// Returns true iff the given index represents a left child.
|
||||
|
#[inline]
|
||||
|
fn is_left_child(index: usize) -> bool {
|
||||
|
index % 2 == 1
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the index of the parent, given an index.
|
||||
|
#[inline]
|
||||
|
fn parent(index: usize) -> Option<usize> {
|
||||
|
if index > 0 {
|
||||
|
Some((index - 1) >> 1)
|
||||
|
} else {
|
||||
|
None
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn convert_index_to_last_level(index: usize, tree_height: usize) -> usize {
|
||||
|
index + (1 << (tree_height - 1)) - 1
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the output hash, given a left and right hash value.
|
||||
|
pub(crate) fn hash_inner_node<H: FixedLengthCRH>(
|
||||
|
parameters: &H::Parameters,
|
||||
|
left: &H::Output,
|
||||
|
right: &H::Output,
|
||||
|
buffer: &mut [u8],
|
||||
|
) -> Result<H::Output, Error> {
|
||||
|
use std::io::Cursor;
|
||||
|
let mut writer = Cursor::new(buffer);
|
||||
|
// Construct left input.
|
||||
|
left.write(&mut writer)?;
|
||||
|
|
||||
|
// Construct right input.
|
||||
|
right.write(&mut writer)?;
|
||||
|
|
||||
|
let buffer = writer.into_inner();
|
||||
|
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
|
||||
|
}
|
||||
|
|
||||
|
/// Returns the hash of a leaf.
|
||||
|
pub(crate) fn hash_leaf<H: FixedLengthCRH, L: ToBytes>(
|
||||
|
parameters: &H::Parameters,
|
||||
|
leaf: &L,
|
||||
|
buffer: &mut [u8],
|
||||
|
) -> Result<H::Output, Error> {
|
||||
|
use std::io::Cursor;
|
||||
|
let mut writer = Cursor::new(buffer);
|
||||
|
leaf.write(&mut writer)?;
|
||||
|
|
||||
|
let buffer = writer.into_inner();
|
||||
|
H::evaluate(parameters, &buffer[..(H::INPUT_SIZE_BITS / 8)])
|
||||
|
}
|
||||
|
|
||||
|
pub(crate) fn hash_empty<H: FixedLengthCRH>(
|
||||
|
parameters: &H::Parameters,
|
||||
|
) -> Result<H::Output, Error> {
|
||||
|
let empty_buffer = vec![0u8; H::INPUT_SIZE_BITS / 8];
|
||||
|
H::evaluate(parameters, &empty_buffer)
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use crate::{crh::{pedersen::*, *}, mht::*};
|
||||
|
use algebra::curves::jubjub::JubJubAffine as JubJub;
|
||||
|
use rand::SeedableRng;
|
||||
|
use rand_xorshift::XorShiftRng;
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub(super) struct Window4x256;
|
||||
|
impl PedersenWindow for Window4x256 {
|
||||
|
const WINDOW_SIZE: usize = 4;
|
||||
|
const NUM_WINDOWS: usize = 256;
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
type H = PedersenCRH<JubJub, Window4x256>;
|
||||
|
|
||||
|
struct JubJubMHTParams;
|
||||
|
|
||||
|
impl MHTParameters for JubJubMHTParams {
|
||||
|
const HEIGHT: usize = 32;
|
||||
|
type H = H;
|
||||
|
}
|
||||
|
type JubJubMHT = MerkleHashTree<JubJubMHTParams>;
|
||||
|
|
||||
|
|
||||
|
fn generate_merkle_tree<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(9174123u64);
|
||||
|
|
||||
|
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
|
||||
|
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
|
||||
|
let root = tree.root();
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
let proof = tree.generate_proof(i, &leaf).unwrap();
|
||||
|
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn mht_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..4u8 {
|
||||
|
leaves.push([i, i, i, i, i, i, i, i]);
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves);
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..100u8 {
|
||||
|
leaves.push([i, i, i, i, i, i, i, i]);
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves);
|
||||
|
}
|
||||
|
|
||||
|
fn bad_merkle_tree_verify<L: ToBytes + Clone + Eq>(leaves: &[L]) -> () {
|
||||
|
use algebra::groups::Group;
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(13423423u64);
|
||||
|
|
||||
|
let crh_parameters = Rc::new(H::setup(&mut rng).unwrap());
|
||||
|
let tree = JubJubMHT::new(crh_parameters.clone(), &leaves).unwrap();
|
||||
|
let root = JubJub::zero();
|
||||
|
for (i, leaf) in leaves.iter().enumerate() {
|
||||
|
let proof = tree.generate_proof(i, &leaf).unwrap();
|
||||
|
assert!(proof.verify(&crh_parameters, &root, &leaf).unwrap());
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[should_panic]
|
||||
|
#[test]
|
||||
|
fn bad_root_mht_test() {
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..4u8 {
|
||||
|
leaves.push([i, i, i, i, i, i, i, i]);
|
||||
|
}
|
||||
|
generate_merkle_tree(&leaves);
|
||||
|
let mut leaves = Vec::new();
|
||||
|
for i in 0..100u8 {
|
||||
|
leaves.push([i, i, i, i, i, i, i, i]);
|
||||
|
}
|
||||
|
bad_merkle_tree_verify(&leaves);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,22 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use crate::nizk::NIZK;
|
||||
|
|
||||
|
pub trait NIZKVerifierGadget<N: NIZK, ConstraintF: Field> {
|
||||
|
type VerificationKeyGadget: AllocGadget<N::VerificationParameters, ConstraintF> + ToBytesGadget<ConstraintF>;
|
||||
|
|
||||
|
type ProofGadget: AllocGadget<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;
|
||||
|
}
|
@ -0,0 +1,543 @@ |
|||||
|
use crate::nizk::{gm17::Gm17, NIZKVerifierGadget};
|
||||
|
use algebra::{Field, ToConstraintField, AffineCurve, PairingEngine};
|
||||
|
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use gm17::{Proof, VerifyingKey};
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(bound = "P::G1Gadget: Clone, P::G2Gadget: Clone"))]
|
||||
|
pub struct ProofGadget<
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
> {
|
||||
|
pub a: P::G1Gadget,
|
||||
|
pub b: P::G2Gadget,
|
||||
|
pub c: P::G1Gadget,
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(
|
||||
|
bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
|
||||
|
P::G2PreparedGadget: Clone, "
|
||||
|
))]
|
||||
|
pub struct VerifyingKeyGadget<
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
> {
|
||||
|
pub h_g2: P::G2Gadget,
|
||||
|
pub g_alpha_g1: P::G1Gadget,
|
||||
|
pub h_beta_g2: P::G2Gadget,
|
||||
|
pub g_gamma_g1: P::G1Gadget,
|
||||
|
pub h_gamma_g2: P::G2Gadget,
|
||||
|
pub query: Vec<P::G1Gadget>,
|
||||
|
}
|
||||
|
|
||||
|
impl<
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
> VerifyingKeyGadget<PairingE, ConstraintF, P>
|
||||
|
{
|
||||
|
pub fn prepare<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<PreparedVerifyingKeyGadget<PairingE, ConstraintF, P>, SynthesisError> {
|
||||
|
let mut cs = cs.ns(|| "Preparing verifying key");
|
||||
|
let g_alpha_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_alpha_g1"), &self.g_alpha_g1)?;
|
||||
|
let h_beta_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_beta_g2"), &self.h_beta_g2)?;
|
||||
|
let g_gamma_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_gamma_pc"), &self.g_gamma_g1)?;
|
||||
|
let h_gamma_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_gamma_pc"), &self.h_gamma_g2)?;
|
||||
|
let h_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_pc"), &self.h_g2)?;
|
||||
|
Ok(PreparedVerifyingKeyGadget {
|
||||
|
g_alpha: self.g_alpha_g1.clone(),
|
||||
|
h_beta: self.h_beta_g2.clone(),
|
||||
|
g_alpha_pc,
|
||||
|
h_beta_pc,
|
||||
|
g_gamma_pc,
|
||||
|
h_gamma_pc,
|
||||
|
h_pc,
|
||||
|
query: self.query.clone(),
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(
|
||||
|
bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
|
||||
|
P::G2PreparedGadget: Clone, "
|
||||
|
))]
|
||||
|
pub struct PreparedVerifyingKeyGadget<
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
> {
|
||||
|
pub g_alpha: P::G1Gadget,
|
||||
|
pub h_beta: P::G2Gadget,
|
||||
|
pub g_alpha_pc: P::G1PreparedGadget,
|
||||
|
pub h_beta_pc: P::G2PreparedGadget,
|
||||
|
pub g_gamma_pc: P::G1PreparedGadget,
|
||||
|
pub h_gamma_pc: P::G2PreparedGadget,
|
||||
|
pub h_pc: P::G2PreparedGadget,
|
||||
|
pub query: Vec<P::G1Gadget>,
|
||||
|
}
|
||||
|
|
||||
|
pub struct Gm17VerifierGadget<PairingE, ConstraintF, P>
|
||||
|
where
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
{
|
||||
|
_pairing_engine: PhantomData<PairingE>,
|
||||
|
_engine: PhantomData<ConstraintF>,
|
||||
|
_pairing_gadget: PhantomData<P>,
|
||||
|
}
|
||||
|
|
||||
|
impl<PairingE, ConstraintF, P, C, V> NIZKVerifierGadget<Gm17<PairingE, C, V>, ConstraintF>
|
||||
|
for Gm17VerifierGadget<PairingE, ConstraintF, P>
|
||||
|
where
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
C: ConstraintSynthesizer<PairingE::Fr>,
|
||||
|
V: ToConstraintField<PairingE::Fr>,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
{
|
||||
|
type VerificationKeyGadget = VerifyingKeyGadget<PairingE, ConstraintF, P>;
|
||||
|
type ProofGadget = ProofGadget<PairingE, ConstraintF, P>;
|
||||
|
|
||||
|
fn check_verify<'a, CS, I, T>(
|
||||
|
mut cs: CS,
|
||||
|
vk: &Self::VerificationKeyGadget,
|
||||
|
mut public_inputs: I,
|
||||
|
proof: &Self::ProofGadget,
|
||||
|
) -> Result<(), SynthesisError>
|
||||
|
where
|
||||
|
CS: ConstraintSystem<ConstraintF>,
|
||||
|
I: Iterator<Item = &'a T>,
|
||||
|
T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
|
||||
|
{
|
||||
|
let pvk = vk.prepare(&mut cs.ns(|| "Prepare vk"))?;
|
||||
|
// e(A*G^{alpha}, B*H^{beta}) = e(G^{alpha}, H^{beta}) * e(G^{psi}, H^{gamma}) *
|
||||
|
// e(C, H) where psi = \sum_{i=0}^l input_i pvk.query[i]
|
||||
|
|
||||
|
let g_psi = {
|
||||
|
let mut cs = cs.ns(|| "Process input");
|
||||
|
let mut g_psi = pvk.query[0].clone();
|
||||
|
let mut input_len = 1;
|
||||
|
for (i, (input, b)) in public_inputs
|
||||
|
.by_ref()
|
||||
|
.zip(pvk.query.iter().skip(1))
|
||||
|
.enumerate()
|
||||
|
{
|
||||
|
let input_bits = input.to_bits(cs.ns(|| format!("Input {}", i)))?;
|
||||
|
g_psi = b.mul_bits(cs.ns(|| format!("Mul {}", i)), &g_psi, input_bits.iter())?;
|
||||
|
input_len += 1;
|
||||
|
}
|
||||
|
// Check that the input and the query in the verification are of the
|
||||
|
// same length.
|
||||
|
assert!(input_len == pvk.query.len() && public_inputs.next().is_none());
|
||||
|
g_psi
|
||||
|
};
|
||||
|
|
||||
|
let mut test1_a_g_alpha = proof.a.add(cs.ns(|| "A * G^{alpha}"), &pvk.g_alpha)?;
|
||||
|
let test1_b_h_beta = proof.b.add(cs.ns(|| "B * H^{beta}"), &pvk.h_beta)?;
|
||||
|
|
||||
|
let test1_exp = {
|
||||
|
test1_a_g_alpha = test1_a_g_alpha.negate(cs.ns(|| "neg 1"))?;
|
||||
|
let test1_a_g_alpha_prep = P::prepare_g1(cs.ns(|| "First prep"), &test1_a_g_alpha)?;
|
||||
|
let test1_b_h_beta_prep = P::prepare_g2(cs.ns(|| "Second prep"), &test1_b_h_beta)?;
|
||||
|
|
||||
|
let g_psi_prep = P::prepare_g1(cs.ns(|| "Third prep"), &g_psi)?;
|
||||
|
|
||||
|
let c_prep = P::prepare_g1(cs.ns(|| "Fourth prep"), &proof.c)?;
|
||||
|
|
||||
|
P::miller_loop(
|
||||
|
cs.ns(|| "Miller loop 1"),
|
||||
|
&[
|
||||
|
test1_a_g_alpha_prep,
|
||||
|
g_psi_prep,
|
||||
|
c_prep,
|
||||
|
pvk.g_alpha_pc.clone(),
|
||||
|
],
|
||||
|
&[
|
||||
|
test1_b_h_beta_prep,
|
||||
|
pvk.h_gamma_pc.clone(),
|
||||
|
pvk.h_pc.clone(),
|
||||
|
pvk.h_beta_pc.clone(),
|
||||
|
],
|
||||
|
)?
|
||||
|
};
|
||||
|
|
||||
|
let test1 = P::final_exponentiation(cs.ns(|| "Final Exp 1"), &test1_exp).unwrap();
|
||||
|
|
||||
|
// e(A, H^{gamma}) = e(G^{gamma}, B)
|
||||
|
let test2_exp = {
|
||||
|
let a_prep = P::prepare_g1(cs.ns(|| "Fifth prep"), &proof.a)?;
|
||||
|
// pvk.h_gamma_pc
|
||||
|
//&pvk.g_gamma_pc
|
||||
|
let proof_b = proof.b.negate(cs.ns(|| "Negate b"))?;
|
||||
|
let b_prep = P::prepare_g2(cs.ns(|| "Sixth prep"), &proof_b)?;
|
||||
|
P::miller_loop(
|
||||
|
cs.ns(|| "Miller loop 4"),
|
||||
|
&[a_prep, pvk.g_gamma_pc.clone()],
|
||||
|
&[pvk.h_gamma_pc.clone(), b_prep],
|
||||
|
)?
|
||||
|
};
|
||||
|
let test2 = P::final_exponentiation(cs.ns(|| "Final Exp 2"), &test2_exp)?;
|
||||
|
|
||||
|
let one = P::GTGadget::one(cs.ns(|| "GT One"))?;
|
||||
|
test1.enforce_equal(cs.ns(|| "Test 1"), &one)?;
|
||||
|
test2.enforce_equal(cs.ns(|| "Test 2"), &one)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<PairingE, ConstraintF, P> AllocGadget<VerifyingKey<PairingE>, ConstraintF>
|
||||
|
for VerifyingKeyGadget<PairingE, ConstraintF, P>
|
||||
|
where
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<VerifyingKey<PairingE>>,
|
||||
|
{
|
||||
|
value_gen().and_then(|vk| {
|
||||
|
let VerifyingKey {
|
||||
|
h_g2,
|
||||
|
g_alpha_g1,
|
||||
|
h_beta_g2,
|
||||
|
g_gamma_g1,
|
||||
|
h_gamma_g2,
|
||||
|
query,
|
||||
|
} = vk.borrow().clone();
|
||||
|
let h_g2 = P::G2Gadget::alloc(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?;
|
||||
|
let g_alpha_g1 =
|
||||
|
P::G1Gadget::alloc(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?;
|
||||
|
let h_beta_g2 =
|
||||
|
P::G2Gadget::alloc(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?;
|
||||
|
let g_gamma_g1 =
|
||||
|
P::G1Gadget::alloc(cs.ns(|| "g_gamma_g1"), || Ok(g_gamma_g1.into_projective()))?;
|
||||
|
let h_gamma_g2 =
|
||||
|
P::G2Gadget::alloc(cs.ns(|| "h_gamma_g2"), || Ok(h_gamma_g2.into_projective()))?;
|
||||
|
|
||||
|
let query = query
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
.map(|(i, query_i)| {
|
||||
|
P::G1Gadget::alloc(cs.ns(|| format!("query_{}", i)), || {
|
||||
|
Ok(query_i.into_projective())
|
||||
|
})
|
||||
|
})
|
||||
|
.collect::<Vec<_>>()
|
||||
|
.into_iter()
|
||||
|
.collect::<Result<_, _>>()?;
|
||||
|
Ok(Self {
|
||||
|
h_g2,
|
||||
|
g_alpha_g1,
|
||||
|
h_beta_g2,
|
||||
|
g_gamma_g1,
|
||||
|
h_gamma_g2,
|
||||
|
query,
|
||||
|
})
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<VerifyingKey<PairingE>>,
|
||||
|
{
|
||||
|
value_gen().and_then(|vk| {
|
||||
|
let VerifyingKey {
|
||||
|
h_g2,
|
||||
|
g_alpha_g1,
|
||||
|
h_beta_g2,
|
||||
|
g_gamma_g1,
|
||||
|
h_gamma_g2,
|
||||
|
query,
|
||||
|
} = vk.borrow().clone();
|
||||
|
let h_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?;
|
||||
|
let g_alpha_g1 =
|
||||
|
P::G1Gadget::alloc_input(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?;
|
||||
|
let h_beta_g2 =
|
||||
|
P::G2Gadget::alloc_input(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?;
|
||||
|
let g_gamma_g1 = P::G1Gadget::alloc_input(cs.ns(|| "g_gamma_g1"), || {
|
||||
|
Ok(g_gamma_g1.into_projective())
|
||||
|
})?;
|
||||
|
let h_gamma_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_gamma_g2"), || {
|
||||
|
Ok(h_gamma_g2.into_projective())
|
||||
|
})?;
|
||||
|
|
||||
|
let query = query
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
.map(|(i, query_i)| {
|
||||
|
P::G1Gadget::alloc_input(cs.ns(|| format!("query_{}", i)), || {
|
||||
|
Ok(query_i.into_projective())
|
||||
|
})
|
||||
|
})
|
||||
|
.collect::<Vec<_>>()
|
||||
|
.into_iter()
|
||||
|
.collect::<Result<_, _>>()?;
|
||||
|
Ok(Self {
|
||||
|
h_g2,
|
||||
|
g_alpha_g1,
|
||||
|
h_beta_g2,
|
||||
|
g_gamma_g1,
|
||||
|
h_gamma_g2,
|
||||
|
query,
|
||||
|
})
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<PairingE, ConstraintF, P> AllocGadget<Proof<PairingE>, ConstraintF>
|
||||
|
for ProofGadget<PairingE, ConstraintF, P>
|
||||
|
where
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Proof<PairingE>>,
|
||||
|
{
|
||||
|
value_gen().and_then(|proof| {
|
||||
|
let Proof { a, b, c } = proof.borrow().clone();
|
||||
|
let a = P::G1Gadget::alloc_checked(cs.ns(|| "a"), || Ok(a.into_projective()))?;
|
||||
|
let b = P::G2Gadget::alloc_checked(cs.ns(|| "b"), || Ok(b.into_projective()))?;
|
||||
|
let c = P::G1Gadget::alloc_checked(cs.ns(|| "c"), || Ok(c.into_projective()))?;
|
||||
|
Ok(Self { a, b, c })
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
value_gen: FN,
|
||||
|
) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
FN: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<Proof<PairingE>>,
|
||||
|
{
|
||||
|
value_gen().and_then(|proof| {
|
||||
|
let Proof { a, b, c } = proof.borrow().clone();
|
||||
|
// We don't need to check here because the prime order check can be performed
|
||||
|
// in plain.
|
||||
|
let a = P::G1Gadget::alloc_input(cs.ns(|| "a"), || Ok(a.into_projective()))?;
|
||||
|
let b = P::G2Gadget::alloc_input(cs.ns(|| "b"), || Ok(b.into_projective()))?;
|
||||
|
let c = P::G1Gadget::alloc_input(cs.ns(|| "c"), || Ok(c.into_projective()))?;
|
||||
|
Ok(Self { a, b, c })
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<PairingE, ConstraintF, P> ToBytesGadget<ConstraintF>
|
||||
|
for VerifyingKeyGadget<PairingE, ConstraintF, P>
|
||||
|
where
|
||||
|
PairingE: PairingEngine,
|
||||
|
ConstraintF: Field,
|
||||
|
P: PairingGadget<PairingE, ConstraintF>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
let mut bytes = Vec::new();
|
||||
|
bytes.extend_from_slice(&self.h_g2.to_bytes(&mut cs.ns(|| "h_g2 to bytes"))?);
|
||||
|
bytes.extend_from_slice(
|
||||
|
&self
|
||||
|
.g_alpha_g1
|
||||
|
.to_bytes(&mut cs.ns(|| "g_alpha_g1 to bytes"))?,
|
||||
|
);
|
||||
|
bytes.extend_from_slice(
|
||||
|
&self
|
||||
|
.h_beta_g2
|
||||
|
.to_bytes(&mut cs.ns(|| "h_beta_g2 to bytes"))?,
|
||||
|
);
|
||||
|
bytes.extend_from_slice(
|
||||
|
&self
|
||||
|
.g_gamma_g1
|
||||
|
.to_bytes(&mut cs.ns(|| "g_gamma_g1 to bytes"))?,
|
||||
|
);
|
||||
|
bytes.extend_from_slice(
|
||||
|
&self
|
||||
|
.h_gamma_g2
|
||||
|
.to_bytes(&mut cs.ns(|| "h_gamma_g2 to bytes"))?,
|
||||
|
);
|
||||
|
for (i, q) in self.query.iter().enumerate() {
|
||||
|
let mut cs = cs.ns(|| format!("Iteration {}", i));
|
||||
|
bytes.extend_from_slice(&q.to_bytes(&mut cs.ns(|| "q"))?);
|
||||
|
}
|
||||
|
Ok(bytes)
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use gm17::*;
|
||||
|
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use super::*;
|
||||
|
use algebra::{
|
||||
|
curves::bls12_377::Bls12_377,
|
||||
|
fields::bls12_377::Fr,
|
||||
|
fields::bls12_377::Fq,
|
||||
|
BitIterator, PrimeField,
|
||||
|
};
|
||||
|
use rand::{thread_rng, Rng};
|
||||
|
use r1cs_std::{
|
||||
|
boolean::Boolean, pairing::bls12_377::PairingGadget as Bls12_377PairingGadget,
|
||||
|
test_constraint_system::TestConstraintSystem,
|
||||
|
};
|
||||
|
|
||||
|
type TestProofSystem = Gm17<Bls12_377, Bench<Fr>, Fr>;
|
||||
|
type TestVerifierGadget = Gm17VerifierGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
|
||||
|
type TestProofGadget = ProofGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
|
||||
|
type TestVkGadget = VerifyingKeyGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
|
||||
|
|
||||
|
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(())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn gm17_verifier_test() {
|
||||
|
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 = {
|
||||
|
let c = Bench::<Fr> {
|
||||
|
inputs: vec![None; num_inputs],
|
||||
|
num_constraints,
|
||||
|
};
|
||||
|
|
||||
|
generate_random_parameters(c, rng).unwrap()
|
||||
|
};
|
||||
|
|
||||
|
{
|
||||
|
let proof = {
|
||||
|
// Create an instance of our circuit (with the
|
||||
|
// witness)
|
||||
|
let c = Bench {
|
||||
|
inputs: inputs.clone(),
|
||||
|
num_constraints,
|
||||
|
};
|
||||
|
// Create a gm17 proof with our parameters.
|
||||
|
create_random_proof(c, ¶ms, rng).unwrap()
|
||||
|
};
|
||||
|
|
||||
|
// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
|
||||
|
let mut cs = TestConstraintSystem::<Fq>::new();
|
||||
|
|
||||
|
let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
|
||||
|
let mut input_gadgets = Vec::new();
|
||||
|
|
||||
|
{
|
||||
|
let mut cs = cs.ns(|| "Allocate Input");
|
||||
|
for (i, input) in inputs.into_iter().enumerate() {
|
||||
|
let mut input_bits = BitIterator::new(input.into_repr()).collect::<Vec<_>>();
|
||||
|
// Input must be in little-endian, but BitIterator outputs in big-endian.
|
||||
|
input_bits.reverse();
|
||||
|
|
||||
|
let input_bits =
|
||||
|
Vec::<Boolean>::alloc_input(cs.ns(|| format!("Input {}", i)), || {
|
||||
|
Ok(input_bits)
|
||||
|
})
|
||||
|
.unwrap();
|
||||
|
input_gadgets.push(input_bits);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let vk_gadget = TestVkGadget::alloc_input(cs.ns(|| "Vk"), || Ok(¶ms.vk)).unwrap();
|
||||
|
let proof_gadget =
|
||||
|
TestProofGadget::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap();
|
||||
|
println!("Time to verify!\n\n\n\n");
|
||||
|
<TestVerifierGadget as NIZKVerifierGadget<TestProofSystem, Fq>>::check_verify(
|
||||
|
cs.ns(|| "Verify"),
|
||||
|
&vk_gadget,
|
||||
|
input_gadgets.iter(),
|
||||
|
&proof_gadget,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!("=========================================================");
|
||||
|
println!("Unsatisfied constraints:");
|
||||
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||
|
println!("=========================================================");
|
||||
|
}
|
||||
|
|
||||
|
// cs.print_named_objects();
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,81 @@ |
|||||
|
use algebra::PairingEngine;
|
||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
use gm17::{
|
||||
|
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
|
||||
|
Parameters, PreparedVerifyingKey, Proof, VerifyingKey,
|
||||
|
};
|
||||
|
use r1cs_core::ConstraintSynthesizer;
|
||||
|
|
||||
|
use algebra::ToConstraintField;
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
use super::NIZK;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
/// Note: V should serialize its contents to `Vec<E::Fr>` in the same order as
|
||||
|
/// during the constraint generation.
|
||||
|
pub struct Gm17<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> {
|
||||
|
#[doc(hidden)]
|
||||
|
_engine: PhantomData<E>,
|
||||
|
#[doc(hidden)]
|
||||
|
_circuit: PhantomData<C>,
|
||||
|
#[doc(hidden)]
|
||||
|
_verifier_input: PhantomData<V>,
|
||||
|
}
|
||||
|
|
||||
|
impl<E: PairingEngine, C: ConstraintSynthesizer<E::Fr>, V: ToConstraintField<E::Fr> + ?Sized> NIZK for Gm17<E, C, V> {
|
||||
|
type Circuit = C;
|
||||
|
type AssignedCircuit = C;
|
||||
|
type ProvingParameters = Parameters<E>;
|
||||
|
type VerificationParameters = VerifyingKey<E>;
|
||||
|
type PreparedVerificationParameters = PreparedVerifyingKey<E>;
|
||||
|
type VerifierInput = V;
|
||||
|
type Proof = Proof<E>;
|
||||
|
|
||||
|
fn setup<R: Rng>(
|
||||
|
circuit: Self::Circuit,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<
|
||||
|
(
|
||||
|
Self::ProvingParameters,
|
||||
|
Self::PreparedVerificationParameters,
|
||||
|
),
|
||||
|
Error,
|
||||
|
> {
|
||||
|
let nizk_time = start_timer!(|| "{Groth-Maller 2017}::Setup");
|
||||
|
let pp = generate_random_parameters::<E, Self::Circuit, R>(circuit, rng)?;
|
||||
|
let vk = prepare_verifying_key(&pp.vk);
|
||||
|
end_timer!(nizk_time);
|
||||
|
Ok((pp, vk))
|
||||
|
}
|
||||
|
|
||||
|
fn prove<R: Rng>(
|
||||
|
pp: &Self::ProvingParameters,
|
||||
|
input_and_witness: Self::AssignedCircuit,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<Self::Proof, Error> {
|
||||
|
let proof_time = start_timer!(|| "{Groth-Maller 2017}::Prove");
|
||||
|
let result = create_random_proof::<E, _, _>(input_and_witness, pp, rng)?;
|
||||
|
end_timer!(proof_time);
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
|
||||
|
fn verify(
|
||||
|
vk: &Self::PreparedVerificationParameters,
|
||||
|
input: &Self::VerifierInput,
|
||||
|
proof: &Self::Proof,
|
||||
|
) -> Result<bool, Error> {
|
||||
|
let verify_time = start_timer!(|| "{Groth-Maller 2017}::Verify");
|
||||
|
let conversion_time = start_timer!(|| "Convert input to E::Fr");
|
||||
|
let input = input.to_field_elements()?;
|
||||
|
end_timer!(conversion_time);
|
||||
|
let verification = start_timer!(|| format!("Verify proof w/ input len: {}", input.len()));
|
||||
|
let result = verify_proof(&vk, proof, &input)?;
|
||||
|
end_timer!(verification);
|
||||
|
end_timer!(verify_time);
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,112 @@ |
|||||
|
use algebra::bytes::ToBytes;
|
||||
|
use rand::Rng;
|
||||
|
|
||||
|
#[cfg(feature = "gm17")]
|
||||
|
pub mod gm17;
|
||||
|
#[cfg(feature = "gm17")]
|
||||
|
pub use self::gm17::Gm17;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub use constraints::*;
|
||||
|
|
||||
|
use crate::Error;
|
||||
|
|
||||
|
pub trait NIZK {
|
||||
|
type Circuit;
|
||||
|
type AssignedCircuit;
|
||||
|
type VerifierInput: ?Sized;
|
||||
|
type ProvingParameters: Clone;
|
||||
|
type VerificationParameters: Clone + Default + From<Self::PreparedVerificationParameters>;
|
||||
|
type PreparedVerificationParameters: Clone + Default + From<Self::VerificationParameters>;
|
||||
|
type Proof: ToBytes + Clone + Default;
|
||||
|
|
||||
|
fn setup<R: Rng>(
|
||||
|
circuit: Self::Circuit,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<
|
||||
|
(
|
||||
|
Self::ProvingParameters,
|
||||
|
Self::PreparedVerificationParameters,
|
||||
|
),
|
||||
|
Error,
|
||||
|
>;
|
||||
|
|
||||
|
fn prove<R: Rng>(
|
||||
|
parameter: &Self::ProvingParameters,
|
||||
|
input_and_witness: Self::AssignedCircuit,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<Self::Proof, Error>;
|
||||
|
|
||||
|
fn verify(
|
||||
|
verifier_key: &Self::PreparedVerificationParameters,
|
||||
|
input: &Self::VerifierInput,
|
||||
|
proof: &Self::Proof,
|
||||
|
) -> Result<bool, Error>;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(all(feature = "gm17", test))]
|
||||
|
mod test {
|
||||
|
use rand::thread_rng;
|
||||
|
use std::ops::AddAssign;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_gm17() {
|
||||
|
use crate::nizk::{gm17::Gm17, NIZK};
|
||||
|
use algebra::{curves::bls12_381::Bls12_381, fields::bls12_381::Fr, Field};
|
||||
|
use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
#[derive(Copy, Clone)]
|
||||
|
struct R1CSCircuit {
|
||||
|
x: Option<Fr>,
|
||||
|
sum: Option<Fr>,
|
||||
|
w: Option<Fr>,
|
||||
|
}
|
||||
|
|
||||
|
impl R1CSCircuit {
|
||||
|
pub(super) fn new(x: Fr, sum: Fr, w: Fr) -> Self {
|
||||
|
Self {
|
||||
|
x: Some(x),
|
||||
|
sum: Some(sum),
|
||||
|
w: Some(w),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl ConstraintSynthesizer<Fr> for R1CSCircuit {
|
||||
|
fn generate_constraints<CS: ConstraintSystem<Fr>>(
|
||||
|
self,
|
||||
|
cs: &mut CS,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
let input = cs.alloc_input(|| "x", || Ok(self.x.unwrap()))?;
|
||||
|
let sum = cs.alloc_input(|| "sum", || Ok(self.sum.unwrap()))?;
|
||||
|
let witness = cs.alloc(|| "w", || Ok(self.w.unwrap()))?;
|
||||
|
|
||||
|
cs.enforce(
|
||||
|
|| "check_one",
|
||||
|
|lc| lc + sum,
|
||||
|
|lc| lc + CS::one(),
|
||||
|
|lc| lc + input + witness,
|
||||
|
);
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let mut sum = Fr::one();
|
||||
|
sum.add_assign(&Fr::one());
|
||||
|
let circuit = R1CSCircuit::new(Fr::one(), sum, Fr::one());
|
||||
|
|
||||
|
let rng = &mut thread_rng();
|
||||
|
|
||||
|
let parameters = Gm17::<Bls12_381, R1CSCircuit, [Fr]>::setup(circuit, rng).unwrap();
|
||||
|
|
||||
|
let proof =
|
||||
|
Gm17::<Bls12_381, R1CSCircuit, [Fr]>::prove(¶meters.0, circuit, rng).unwrap();
|
||||
|
|
||||
|
let result =
|
||||
|
Gm17::<Bls12_381, R1CSCircuit, [Fr]>::verify(¶meters.1, &[Fr::one(), sum], &proof)
|
||||
|
.unwrap();
|
||||
|
assert!(result);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,650 @@ |
|||||
|
use algebra::PrimeField;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use crate::prf::PRFGadget;
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use std::borrow::Borrow;
|
||||
|
|
||||
|
// 2.1. Parameters
|
||||
|
// The following table summarizes various parameters and their ranges:
|
||||
|
// | BLAKE2b | BLAKE2s |
|
||||
|
// --------------+------------------+------------------+
|
||||
|
// Bits in word | w = 64 | w = 32 |
|
||||
|
// Rounds in F | r = 12 | r = 10 |
|
||||
|
// Block bytes | bb = 128 | bb = 64 |
|
||||
|
// Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 |
|
||||
|
// Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 |
|
||||
|
// Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 |
|
||||
|
// --------------+------------------+------------------+
|
||||
|
// G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) |
|
||||
|
// constants = | (32, 24, 16, 63) | (16, 12, 8, 7) |
|
||||
|
// --------------+------------------+------------------+
|
||||
|
//
|
||||
|
|
||||
|
const R1: usize = 16;
|
||||
|
const R2: usize = 12;
|
||||
|
const R3: usize = 8;
|
||||
|
const R4: usize = 7;
|
||||
|
|
||||
|
// Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||
|
// ----------+-------------------------------------------------+
|
||||
|
// SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
||||
|
// SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 |
|
||||
|
// SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 |
|
||||
|
// SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 |
|
||||
|
// SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 |
|
||||
|
// SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 |
|
||||
|
// SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 |
|
||||
|
// SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 |
|
||||
|
// SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 |
|
||||
|
// SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 |
|
||||
|
// ----------+-------------------------------------------------+
|
||||
|
//
|
||||
|
|
||||
|
const SIGMA: [[usize; 16]; 10] = [
|
||||
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
||||
|
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
||||
|
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
||||
|
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
||||
|
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
||||
|
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
||||
|
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
||||
|
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
||||
|
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
||||
|
];
|
||||
|
|
||||
|
// 3.1. Mixing Function G
|
||||
|
// The G primitive function mixes two input words, "x" and "y", into
|
||||
|
// four words indexed by "a", "b", "c", and "d" in the working vector
|
||||
|
// v[0..15]. The full modified vector is returned. The rotation
|
||||
|
// constants (R1, R2, R3, R4) are given in Section 2.1.
|
||||
|
// FUNCTION G( v[0..15], a, b, c, d, x, y )
|
||||
|
// |
|
||||
|
// | v[a] := (v[a] + v[b] + x) mod 2**w
|
||||
|
// | v[d] := (v[d] ^ v[a]) >>> R1
|
||||
|
// | v[c] := (v[c] + v[d]) mod 2**w
|
||||
|
// | v[b] := (v[b] ^ v[c]) >>> R2
|
||||
|
// | v[a] := (v[a] + v[b] + y) mod 2**w
|
||||
|
// | v[d] := (v[d] ^ v[a]) >>> R3
|
||||
|
// | v[c] := (v[c] + v[d]) mod 2**w
|
||||
|
// | v[b] := (v[b] ^ v[c]) >>> R4
|
||||
|
// |
|
||||
|
// | RETURN v[0..15]
|
||||
|
// |
|
||||
|
// END FUNCTION.
|
||||
|
//
|
||||
|
|
||||
|
fn mixing_g<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
v: &mut [UInt32],
|
||||
|
a: usize,
|
||||
|
b: usize,
|
||||
|
c: usize,
|
||||
|
d: usize,
|
||||
|
x: &UInt32,
|
||||
|
y: &UInt32,
|
||||
|
) -> 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);
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
// 3.2. Compression Function F
|
||||
|
// Compression function F takes as an argument the state vector "h",
|
||||
|
// message block vector "m" (last block is padded with zeros to full
|
||||
|
// block size, if required), 2w-bit_gadget offset counter "t", and final block
|
||||
|
// indicator flag "f". Local vector v[0..15] is used in processing. F
|
||||
|
// returns a new state vector. The number of rounds, "r", is 12 for
|
||||
|
// BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1.
|
||||
|
// FUNCTION F( h[0..7], m[0..15], t, f )
|
||||
|
// |
|
||||
|
// | // Initialize local work vector v[0..15]
|
||||
|
// | v[0..7] := h[0..7] // First half from state.
|
||||
|
// | v[8..15] := IV[0..7] // Second half from IV.
|
||||
|
// |
|
||||
|
// | v[12] := v[12] ^ (t mod 2**w) // Low word of the offset.
|
||||
|
// | v[13] := v[13] ^ (t >> w) // High word.
|
||||
|
// |
|
||||
|
// | IF f = TRUE THEN // last block flag?
|
||||
|
// | | v[14] := v[14] ^ 0xFF..FF // Invert all bits.
|
||||
|
// | END IF.
|
||||
|
// |
|
||||
|
// | // Cryptographic mixing
|
||||
|
// | FOR i = 0 TO r - 1 DO // Ten or twelve rounds.
|
||||
|
// | |
|
||||
|
// | | // Message word selection permutation for this round.
|
||||
|
// | | s[0..15] := SIGMA[i mod 10][0..15]
|
||||
|
// | |
|
||||
|
// | | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] )
|
||||
|
// | | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] )
|
||||
|
// | | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] )
|
||||
|
// | | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] )
|
||||
|
// | |
|
||||
|
// | | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] )
|
||||
|
// | | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] )
|
||||
|
// | | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] )
|
||||
|
// | | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] )
|
||||
|
// | |
|
||||
|
// | END FOR
|
||||
|
// |
|
||||
|
// | FOR i = 0 TO 7 DO // XOR the two halves.
|
||||
|
// | | h[i] := h[i] ^ v[i] ^ v[i + 8]
|
||||
|
// | END FOR.
|
||||
|
// |
|
||||
|
// | RETURN h[0..7] // New state.
|
||||
|
// |
|
||||
|
// END FUNCTION.
|
||||
|
//
|
||||
|
|
||||
|
fn blake2s_compression<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
h: &mut [UInt32],
|
||||
|
m: &[UInt32],
|
||||
|
t: u64,
|
||||
|
f: bool,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
assert_eq!(h.len(), 8);
|
||||
|
assert_eq!(m.len(), 16);
|
||||
|
|
||||
|
// static const uint32_t blake2s_iv[8] =
|
||||
|
// {
|
||||
|
// 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||
|
// 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
|
// };
|
||||
|
//
|
||||
|
|
||||
|
let mut v = Vec::with_capacity(16);
|
||||
|
v.extend_from_slice(h);
|
||||
|
v.push(UInt32::constant(0x6A09E667));
|
||||
|
v.push(UInt32::constant(0xBB67AE85));
|
||||
|
v.push(UInt32::constant(0x3C6EF372));
|
||||
|
v.push(UInt32::constant(0xA54FF53A));
|
||||
|
v.push(UInt32::constant(0x510E527F));
|
||||
|
v.push(UInt32::constant(0x9B05688C));
|
||||
|
v.push(UInt32::constant(0x1F83D9AB));
|
||||
|
v.push(UInt32::constant(0x5BE0CD19));
|
||||
|
|
||||
|
assert_eq!(v.len(), 16);
|
||||
|
|
||||
|
v[12] = v[12].xor(cs.ns(|| "first xor"), &UInt32::constant(t as u32))?;
|
||||
|
v[13] = v[13].xor(cs.ns(|| "second xor"), &UInt32::constant((t >> 32) as u32))?;
|
||||
|
|
||||
|
if f {
|
||||
|
v[14] = v[14].xor(cs.ns(|| "third 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]],
|
||||
|
)?;
|
||||
|
}
|
||||
|
|
||||
|
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])?;
|
||||
|
}
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
// FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
|
||||
|
// |
|
||||
|
// | h[0..7] := IV[0..7] // Initialization Vector.
|
||||
|
// |
|
||||
|
// | // Parameter block p[0]
|
||||
|
// | h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn
|
||||
|
// |
|
||||
|
// | // Process padded key and data blocks
|
||||
|
// | IF dd > 1 THEN
|
||||
|
// | | FOR i = 0 TO dd - 2 DO
|
||||
|
// | | | h := F( h, d[i], (i + 1) * bb, FALSE )
|
||||
|
// | | END FOR.
|
||||
|
// | END IF.
|
||||
|
// |
|
||||
|
// | // Final block.
|
||||
|
// | IF kk = 0 THEN
|
||||
|
// | | h := F( h, d[dd - 1], ll, TRUE )
|
||||
|
// | ELSE
|
||||
|
// | | h := F( h, d[dd - 1], ll + bb, TRUE )
|
||||
|
// | END IF.
|
||||
|
// |
|
||||
|
// | RETURN first "nn" bytes from little-endian word array h[].
|
||||
|
// |
|
||||
|
// END FUNCTION.
|
||||
|
//
|
||||
|
|
||||
|
pub fn blake2s_gadget<ConstraintF: PrimeField, CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
input: &[Boolean],
|
||||
|
) -> Result<Vec<UInt32>, SynthesisError> {
|
||||
|
assert!(input.len() % 8 == 0);
|
||||
|
|
||||
|
let mut h = Vec::with_capacity(8);
|
||||
|
h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32));
|
||||
|
h.push(UInt32::constant(0xBB67AE85));
|
||||
|
h.push(UInt32::constant(0x3C6EF372));
|
||||
|
h.push(UInt32::constant(0xA54FF53A));
|
||||
|
h.push(UInt32::constant(0x510E527F));
|
||||
|
h.push(UInt32::constant(0x9B05688C));
|
||||
|
h.push(UInt32::constant(0x1F83D9AB));
|
||||
|
h.push(UInt32::constant(0x5BE0CD19));
|
||||
|
|
||||
|
let mut blocks: Vec<Vec<UInt32>> = vec![];
|
||||
|
|
||||
|
for block in input.chunks(512) {
|
||||
|
let mut this_block = Vec::with_capacity(16);
|
||||
|
for word in block.chunks(32) {
|
||||
|
let mut tmp = word.to_vec();
|
||||
|
while tmp.len() < 32 {
|
||||
|
tmp.push(Boolean::constant(false));
|
||||
|
}
|
||||
|
this_block.push(UInt32::from_bits_le(&tmp));
|
||||
|
}
|
||||
|
while this_block.len() < 16 {
|
||||
|
this_block.push(UInt32::constant(0));
|
||||
|
}
|
||||
|
blocks.push(this_block);
|
||||
|
}
|
||||
|
|
||||
|
if blocks.is_empty() {
|
||||
|
blocks.push((0..16).map(|_| UInt32::constant(0)).collect());
|
||||
|
}
|
||||
|
|
||||
|
for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() {
|
||||
|
let cs = cs.ns(|| format!("block {}", i));
|
||||
|
|
||||
|
blake2s_compression(cs, &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,
|
||||
|
)?;
|
||||
|
}
|
||||
|
|
||||
|
Ok(h)
|
||||
|
}
|
||||
|
|
||||
|
use crate::prf::Blake2s;
|
||||
|
|
||||
|
pub struct Blake2sGadget;
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct Blake2sOutputGadget(pub Vec<UInt8>);
|
||||
|
|
||||
|
impl PartialEq for Blake2sOutputGadget {
|
||||
|
fn eq(&self, other: &Self) -> bool {
|
||||
|
self.0 == 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>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> 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(())
|
||||
|
}
|
||||
|
|
||||
|
fn cost() -> usize {
|
||||
|
32 * <UInt8 as ConditionalEqGadget<ConstraintF>>::cost()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Blake2sOutputGadget {
|
||||
|
#[inline]
|
||||
|
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
Ok(self.0.clone())
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.to_bytes(cs)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<ConstraintF: PrimeField> AllocGadget<[u8; 32], ConstraintF> for Blake2sOutputGadget {
|
||||
|
#[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;
|
||||
|
|
||||
|
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 check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
seed: &[UInt8],
|
||||
|
input: &[UInt8],
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
|
assert_eq!(seed.len(), 32);
|
||||
|
// assert_eq!(input.len(), 32);
|
||||
|
let mut gadget_input = Vec::with_capacity(512);
|
||||
|
for byte in seed.into_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)?
|
||||
|
.into_iter()
|
||||
|
.enumerate()
|
||||
|
{
|
||||
|
let chunk = int.to_bytes(&mut cs.ns(|| format!("Result ToBytes {}", i)))?;
|
||||
|
result.extend_from_slice(&chunk);
|
||||
|
}
|
||||
|
Ok(Blake2sOutputGadget(result))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use algebra::fields::bls12_377::fr::Fr;
|
||||
|
use digest::{FixedOutput, Input};
|
||||
|
use rand::{Rng, SeedableRng};
|
||||
|
use rand_xorshift::XorShiftRng;
|
||||
|
|
||||
|
use crate::prf::blake2s::{Blake2s as B2SPRF, constraints::blake2s_gadget};
|
||||
|
use blake2::Blake2s;
|
||||
|
use r1cs_core::ConstraintSystem;
|
||||
|
|
||||
|
use super::Blake2sGadget;
|
||||
|
use r1cs_std::{prelude::*, boolean::AllocatedBit, test_constraint_system::TestConstraintSystem};
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_blake2s_constraints() {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
let input_bits: Vec<_> = (0..512)
|
||||
|
.map(|i| {
|
||||
|
AllocatedBit::alloc(cs.ns(|| format!("input bit_gadget {}", i)), || Ok(true))
|
||||
|
.unwrap()
|
||||
|
.into()
|
||||
|
})
|
||||
|
.collect();
|
||||
|
blake2s_gadget(&mut cs, &input_bits).unwrap();
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
assert_eq!(cs.num_constraints(), 21792);
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_blake2s_prf() {
|
||||
|
use crate::prf::{PRF, PRFGadget};
|
||||
|
use rand::Rng;
|
||||
|
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
let mut seed = [0u8; 32];
|
||||
|
rng.fill(&mut seed);
|
||||
|
|
||||
|
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 out = B2SPRF::evaluate(&seed, &input).unwrap();
|
||||
|
let actual_out_gadget = <Blake2sGadget as PRFGadget<_, Fr>>::OutputGadget::alloc(
|
||||
|
&mut 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();
|
||||
|
|
||||
|
if !cs.is_satisfied() {
|
||||
|
println!(
|
||||
|
"which is unsatisfied: {:?}",
|
||||
|
cs.which_is_unsatisfied().unwrap()
|
||||
|
);
|
||||
|
}
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_blake2s_precomp_constraints() {
|
||||
|
// Test that 512 fixed leading bits (constants)
|
||||
|
// doesn't result in more constraints.
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
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()
|
||||
|
}))
|
||||
|
.collect();
|
||||
|
blake2s_gadget(&mut cs, &input_bits).unwrap();
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
assert_eq!(cs.num_constraints(), 21792);
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_blake2s_constant_constraints() {
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
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();
|
||||
|
assert_eq!(cs.num_constraints(), 0);
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_blake2s() {
|
||||
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||
|
|
||||
|
for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) {
|
||||
|
let mut h = Blake2s::new_keyed(&[], 32);
|
||||
|
|
||||
|
let data: Vec<u8> = (0..input_len).map(|_| rng.gen()).collect();
|
||||
|
|
||||
|
h.process(&data);
|
||||
|
|
||||
|
let hash_result = h.fixed_result();
|
||||
|
|
||||
|
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
|
||||
|
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));
|
||||
|
|
||||
|
input_bits.push(
|
||||
|
AllocatedBit::alloc(cs, || Ok((input_byte >> bit_i) & 1u8 == 1u8))
|
||||
|
.unwrap()
|
||||
|
.into(),
|
||||
|
);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let r = blake2s_gadget(&mut cs, &input_bits).unwrap();
|
||||
|
|
||||
|
assert!(cs.is_satisfied());
|
||||
|
|
||||
|
let mut s = hash_result
|
||||
|
.as_ref()
|
||||
|
.iter()
|
||||
|
.flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8));
|
||||
|
|
||||
|
for chunk in r {
|
||||
|
for b in chunk.to_bits_le() {
|
||||
|
match b {
|
||||
|
Boolean::Is(b) => {
|
||||
|
assert!(s.next().unwrap() == b.get_value().unwrap());
|
||||
|
},
|
||||
|
Boolean::Not(b) => {
|
||||
|
assert!(s.next().unwrap() != b.get_value().unwrap());
|
||||
|
},
|
||||
|
Boolean::Constant(b) => {
|
||||
|
assert!(input_len == 0);
|
||||
|
assert!(s.next().unwrap() == b);
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,28 @@ |
|||||
|
use blake2::Blake2s as b2s;
|
||||
|
use digest::Digest;
|
||||
|
|
||||
|
use super::PRF;
|
||||
|
use crate::CryptoError;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
#[derive(Clone)]
|
||||
|
pub struct Blake2s;
|
||||
|
|
||||
|
impl PRF for Blake2s {
|
||||
|
type Input = [u8; 32];
|
||||
|
type Output = [u8; 32];
|
||||
|
type Seed = [u8; 32];
|
||||
|
|
||||
|
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError> {
|
||||
|
let eval_time = start_timer!(|| "Blake2s::Eval");
|
||||
|
let mut h = b2s::new();
|
||||
|
h.input(seed.as_ref());
|
||||
|
h.input(input.as_ref());
|
||||
|
let mut result = [0u8; 32];
|
||||
|
result.copy_from_slice(&h.result());
|
||||
|
end_timer!(eval_time);
|
||||
|
Ok(result)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,19 @@ |
|||||
|
use algebra::Field;
|
||||
|
use std::fmt::Debug;
|
||||
|
|
||||
|
use crate::prf::PRF;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
pub trait PRFGadget<P: PRF, ConstraintF: Field> {
|
||||
|
type OutputGadget: EqGadget<ConstraintF> + ToBytesGadget<ConstraintF> + AllocGadget<P::Output, ConstraintF> + Clone + Debug;
|
||||
|
|
||||
|
fn new_seed<CS: ConstraintSystem<ConstraintF>>(cs: CS, output: &P::Seed) -> Vec<UInt8>;
|
||||
|
|
||||
|
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
cs: CS,
|
||||
|
seed: &[UInt8],
|
||||
|
input: &[UInt8],
|
||||
|
) -> Result<Self::OutputGadget, SynthesisError>;
|
||||
|
}
|
@ -0,0 +1,20 @@ |
|||||
|
use algebra::bytes::{FromBytes, ToBytes};
|
||||
|
use std::{fmt::Debug, hash::Hash};
|
||||
|
|
||||
|
use crate::CryptoError;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub use constraints::*;
|
||||
|
|
||||
|
pub mod blake2s;
|
||||
|
pub use self::blake2s::*;
|
||||
|
|
||||
|
pub trait PRF {
|
||||
|
type Input: FromBytes + Default;
|
||||
|
type Output: ToBytes + Eq + Clone + Default + Hash;
|
||||
|
type Seed: FromBytes + ToBytes + Clone + Default + Debug;
|
||||
|
|
||||
|
fn evaluate(seed: &Self::Seed, input: &Self::Input) -> Result<Self::Output, CryptoError>;
|
||||
|
}
|
@ -0,0 +1,18 @@ |
|||||
|
use algebra::Field;
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use crate::signature::SignatureScheme;
|
||||
|
|
||||
|
pub trait SigRandomizePkGadget<S: SignatureScheme, ConstraintF: Field> {
|
||||
|
type ParametersGadget: AllocGadget<S::Parameters, ConstraintF> + Clone;
|
||||
|
|
||||
|
type PublicKeyGadget: ToBytesGadget<ConstraintF> + EqGadget<ConstraintF> + AllocGadget<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>;
|
||||
|
}
|
@ -0,0 +1,106 @@ |
|||||
|
use algebra::bytes::ToBytes;
|
||||
|
use crate::Error;
|
||||
|
use rand::Rng;
|
||||
|
use std::hash::Hash;
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub use constraints::*;
|
||||
|
|
||||
|
|
||||
|
pub mod schnorr;
|
||||
|
|
||||
|
pub trait SignatureScheme {
|
||||
|
type Parameters: Clone + Send + Sync;
|
||||
|
type PublicKey: ToBytes + Hash + Eq + Clone + Default + Send + Sync;
|
||||
|
type SecretKey: ToBytes + Clone + Default;
|
||||
|
type Signature: Clone + Default + Send + Sync;
|
||||
|
|
||||
|
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error>;
|
||||
|
|
||||
|
fn keygen<R: Rng>(
|
||||
|
pp: &Self::Parameters,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<(Self::PublicKey, Self::SecretKey), Error>;
|
||||
|
|
||||
|
fn sign<R: Rng>(
|
||||
|
pp: &Self::Parameters,
|
||||
|
sk: &Self::SecretKey,
|
||||
|
message: &[u8],
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<Self::Signature, Error>;
|
||||
|
|
||||
|
fn verify(
|
||||
|
pp: &Self::Parameters,
|
||||
|
pk: &Self::PublicKey,
|
||||
|
message: &[u8],
|
||||
|
signature: &Self::Signature,
|
||||
|
) -> Result<bool, Error>;
|
||||
|
|
||||
|
fn randomize_public_key(
|
||||
|
pp: &Self::Parameters,
|
||||
|
public_key: &Self::PublicKey,
|
||||
|
randomness: &[u8],
|
||||
|
) -> Result<Self::PublicKey, Error>;
|
||||
|
|
||||
|
fn randomize_signature(
|
||||
|
pp: &Self::Parameters,
|
||||
|
signature: &Self::Signature,
|
||||
|
randomness: &[u8],
|
||||
|
) -> Result<Self::Signature, Error>;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use crate::{signature::schnorr::SchnorrSignature, SignatureScheme};
|
||||
|
use algebra::{
|
||||
|
curves::edwards_sw6::EdwardsAffine as Edwards, groups::Group, to_bytes, ToBytes,
|
||||
|
};
|
||||
|
use blake2::Blake2s;
|
||||
|
use rand::thread_rng;
|
||||
|
use algebra::UniformRand;
|
||||
|
|
||||
|
fn sign_and_verify<S: SignatureScheme>(message: &[u8]) {
|
||||
|
let rng = &mut thread_rng();
|
||||
|
let parameters = S::setup::<_>(rng).unwrap();
|
||||
|
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
||||
|
let sig = S::sign(¶meters, &sk, &message, rng).unwrap();
|
||||
|
assert!(S::verify(¶meters, &pk, &message, &sig).unwrap());
|
||||
|
}
|
||||
|
|
||||
|
fn failed_verification<S: SignatureScheme>(message: &[u8], bad_message: &[u8]) {
|
||||
|
let rng = &mut thread_rng();
|
||||
|
let parameters = S::setup::<_>(rng).unwrap();
|
||||
|
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
||||
|
let sig = S::sign(¶meters, &sk, message, rng).unwrap();
|
||||
|
assert!(!S::verify(¶meters, &pk, bad_message, &sig).unwrap());
|
||||
|
}
|
||||
|
|
||||
|
fn randomize_and_verify<S: SignatureScheme>(message: &[u8], randomness: &[u8]) {
|
||||
|
let rng = &mut thread_rng();
|
||||
|
let parameters = S::setup::<_>(rng).unwrap();
|
||||
|
let (pk, sk) = S::keygen(¶meters, rng).unwrap();
|
||||
|
let sig = S::sign(¶meters, &sk, message, rng).unwrap();
|
||||
|
assert!(S::verify(¶meters, &pk, message, &sig).unwrap());
|
||||
|
let randomized_pk = S::randomize_public_key(¶meters, &pk, randomness).unwrap();
|
||||
|
let randomized_sig = S::randomize_signature(¶meters, &sig, randomness).unwrap();
|
||||
|
assert!(S::verify(¶meters, &randomized_pk, &message, &randomized_sig).unwrap());
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn schnorr_signature_test() {
|
||||
|
let message = "Hi, I am a Schnorr signature!";
|
||||
|
let rng = &mut thread_rng();
|
||||
|
sign_and_verify::<SchnorrSignature<Edwards, Blake2s>>(message.as_bytes());
|
||||
|
failed_verification::<SchnorrSignature<Edwards, Blake2s>>(
|
||||
|
message.as_bytes(),
|
||||
|
"Bad message".as_bytes(),
|
||||
|
);
|
||||
|
let random_scalar = to_bytes!(<Edwards as Group>::ScalarField::rand(rng)).unwrap();
|
||||
|
randomize_and_verify::<SchnorrSignature<Edwards, Blake2s>>(
|
||||
|
message.as_bytes(),
|
||||
|
&random_scalar.as_slice(),
|
||||
|
);
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,210 @@ |
|||||
|
use algebra::{groups::Group, Field};
|
||||
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
|
use r1cs_std::prelude::*;
|
||||
|
|
||||
|
use crate::signature::SigRandomizePkGadget;
|
||||
|
|
||||
|
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
|
||||
|
use crate::signature::schnorr::{
|
||||
|
SchnorrPublicKey, SchnorrSigParameters, SchnorrSignature,
|
||||
|
};
|
||||
|
use digest::Digest;
|
||||
|
|
||||
|
pub struct SchnorrSigGadgetParameters<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> {
|
||||
|
generator: GG,
|
||||
|
_group: PhantomData<*const G>,
|
||||
|
_engine: PhantomData<*const ConstraintF>,
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>> Clone
|
||||
|
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
|
||||
|
{
|
||||
|
fn clone(&self) -> Self {
|
||||
|
Self {
|
||||
|
generator: self.generator.clone(),
|
||||
|
_group: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[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>")
|
||||
|
)]
|
||||
|
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>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
D: Digest + Send + Sync,
|
||||
|
ConstraintF: Field,
|
||||
|
{
|
||||
|
type ParametersGadget = SchnorrSigGadgetParameters<G, ConstraintF, GG>;
|
||||
|
type PublicKeyGadget = SchnorrSigGadgetPk<G, ConstraintF, GG>;
|
||||
|
|
||||
|
fn check_randomization_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
mut cs: CS,
|
||||
|
parameters: &Self::ParametersGadget,
|
||||
|
public_key: &Self::PublicKeyGadget,
|
||||
|
randomness: &[UInt8],
|
||||
|
) -> Result<Self::PublicKeyGadget, SynthesisError> {
|
||||
|
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 {
|
||||
|
pub_key: rand_pk,
|
||||
|
_group: PhantomData,
|
||||
|
_engine: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G, ConstraintF, GG, D> AllocGadget<SchnorrSigParameters<G, D>, ConstraintF>
|
||||
|
for SchnorrSigGadgetParameters<G, ConstraintF, GG>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
ConstraintF: Field,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
D: Digest,
|
||||
|
{
|
||||
|
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<SchnorrSigParameters<G, D>>,
|
||||
|
{
|
||||
|
let generator = GG::alloc_checked(cs, || f().map(|pp| pp.borrow().generator))?;
|
||||
|
Ok(Self {
|
||||
|
generator,
|
||||
|
_engine: PhantomData,
|
||||
|
_group: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
|
||||
|
where
|
||||
|
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
|
T: Borrow<SchnorrSigParameters<G, D>>,
|
||||
|
{
|
||||
|
let generator = GG::alloc_input(cs, || f().map(|pp| pp.borrow().generator))?;
|
||||
|
Ok(Self {
|
||||
|
generator,
|
||||
|
_engine: PhantomData,
|
||||
|
_group: PhantomData,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G, ConstraintF, GG> AllocGadget<SchnorrPublicKey<G>, ConstraintF> for SchnorrSigGadgetPk<G, ConstraintF, GG>
|
||||
|
where
|
||||
|
G: Group,
|
||||
|
ConstraintF: Field,
|
||||
|
GG: GroupGadget<G, ConstraintF>,
|
||||
|
{
|
||||
|
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>,
|
||||
|
{
|
||||
|
#[inline]
|
||||
|
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
other: &Self,
|
||||
|
condition: &Boolean,
|
||||
|
) -> Result<(), SynthesisError> {
|
||||
|
self.pub_key.conditional_enforce_equal(
|
||||
|
&mut cs.ns(|| "PubKey equality"),
|
||||
|
&other.pub_key,
|
||||
|
condition,
|
||||
|
)?;
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
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>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.pub_key.to_bytes(&mut cs.ns(|| "PubKey To Bytes"))
|
||||
|
}
|
||||
|
|
||||
|
fn to_bytes_strict<CS: ConstraintSystem<ConstraintF>>(
|
||||
|
&self,
|
||||
|
mut cs: CS,
|
||||
|
) -> Result<Vec<UInt8>, SynthesisError> {
|
||||
|
self.pub_key
|
||||
|
.to_bytes_strict(&mut cs.ns(|| "PubKey To Bytes"))
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,223 @@ |
|||||
|
use crate::SignatureScheme;
|
||||
|
use algebra::{
|
||||
|
bytes::ToBytes,
|
||||
|
fields::{Field, PrimeField},
|
||||
|
groups::Group,
|
||||
|
to_bytes,
|
||||
|
};
|
||||
|
use digest::Digest;
|
||||
|
use crate::Error;
|
||||
|
use algebra::UniformRand;
|
||||
|
use rand::Rng;
|
||||
|
use std::{
|
||||
|
hash::Hash,
|
||||
|
io::{Result as IoResult, Write},
|
||||
|
marker::PhantomData,
|
||||
|
};
|
||||
|
|
||||
|
#[cfg(feature = "r1cs")]
|
||||
|
pub mod constraints;
|
||||
|
|
||||
|
pub struct SchnorrSignature<G: Group, D: Digest> {
|
||||
|
_group: PhantomData<G>,
|
||||
|
_hash: PhantomData<D>,
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(bound = "G: Group, H: Digest"))]
|
||||
|
pub struct SchnorrSigParameters<G: Group, H: Digest> {
|
||||
|
_hash: PhantomData<H>,
|
||||
|
pub generator: G,
|
||||
|
pub salt: [u8; 32],
|
||||
|
}
|
||||
|
|
||||
|
pub type SchnorrPublicKey<G> = G;
|
||||
|
|
||||
|
#[derive(Derivative)]
|
||||
|
#[derivative(Clone(bound = "G: Group"), Default(bound = "G: Group"))]
|
||||
|
pub struct SchnorrSecretKey<G: Group>(pub G::ScalarField);
|
||||
|
|
||||
|
impl<G: Group> ToBytes for SchnorrSecretKey<G> {
|
||||
|
#[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,
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group + Hash, D: Digest + Send + Sync> SignatureScheme for SchnorrSignature<G, D>
|
||||
|
where
|
||||
|
G::ScalarField: PrimeField,
|
||||
|
{
|
||||
|
type Parameters = SchnorrSigParameters<G, D>;
|
||||
|
type PublicKey = G;
|
||||
|
type SecretKey = SchnorrSecretKey<G>;
|
||||
|
type Signature = SchnorrSig<G>;
|
||||
|
|
||||
|
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);
|
||||
|
|
||||
|
end_timer!(setup_time);
|
||||
|
Ok(SchnorrSigParameters {
|
||||
|
_hash: PhantomData,
|
||||
|
generator,
|
||||
|
salt,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn keygen<R: Rng>(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<(Self::PublicKey, Self::SecretKey), Error> {
|
||||
|
let keygen_time = start_timer!(|| "SchnorrSig::KeyGen");
|
||||
|
|
||||
|
let secret_key = G::ScalarField::rand(rng);
|
||||
|
let public_key = parameters.generator.mul(&secret_key);
|
||||
|
|
||||
|
end_timer!(keygen_time);
|
||||
|
Ok((public_key, SchnorrSecretKey(secret_key)))
|
||||
|
}
|
||||
|
|
||||
|
fn sign<R: Rng>(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
sk: &Self::SecretKey,
|
||||
|
message: &[u8],
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<Self::Signature, Error> {
|
||||
|
let sign_time = start_timer!(|| "SchnorrSig::Sign");
|
||||
|
// (k, e);
|
||||
|
let (random_scalar, verifier_challenge) = loop {
|
||||
|
// Sample a random scalar `k` from the prime scalar field.
|
||||
|
let random_scalar: G::ScalarField = G::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);
|
||||
|
|
||||
|
// Hash everything to get verifier challenge.
|
||||
|
let mut hash_input = Vec::new();
|
||||
|
hash_input.extend_from_slice(¶meters.salt);
|
||||
|
hash_input.extend_from_slice(&to_bytes![prover_commitment]?);
|
||||
|
hash_input.extend_from_slice(message);
|
||||
|
|
||||
|
// Compute the supposed verifier response: e := H(salt || r || msg);
|
||||
|
if let Some(verifier_challenge) =
|
||||
|
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
|
||||
|
{
|
||||
|
break (random_scalar, verifier_challenge);
|
||||
|
};
|
||||
|
};
|
||||
|
|
||||
|
// k - xe;
|
||||
|
let prover_response = random_scalar - &(verifier_challenge * &sk.0);
|
||||
|
let signature = SchnorrSig {
|
||||
|
prover_response,
|
||||
|
verifier_challenge,
|
||||
|
};
|
||||
|
|
||||
|
end_timer!(sign_time);
|
||||
|
Ok(signature)
|
||||
|
}
|
||||
|
|
||||
|
fn verify(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
pk: &Self::PublicKey,
|
||||
|
message: &[u8],
|
||||
|
signature: &Self::Signature,
|
||||
|
) -> Result<bool, Error> {
|
||||
|
let verify_time = start_timer!(|| "SchnorrSig::Verify");
|
||||
|
|
||||
|
let SchnorrSig {
|
||||
|
prover_response,
|
||||
|
verifier_challenge,
|
||||
|
} = signature;
|
||||
|
let mut claimed_prover_commitment = parameters.generator.mul(prover_response);
|
||||
|
let public_key_times_verifier_challenge = pk.mul(verifier_challenge);
|
||||
|
claimed_prover_commitment += &public_key_times_verifier_challenge;
|
||||
|
|
||||
|
let mut hash_input = Vec::new();
|
||||
|
hash_input.extend_from_slice(¶meters.salt);
|
||||
|
hash_input.extend_from_slice(&to_bytes![claimed_prover_commitment]?);
|
||||
|
hash_input.extend_from_slice(&message);
|
||||
|
|
||||
|
let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) =
|
||||
|
G::ScalarField::from_random_bytes(&D::digest(&hash_input))
|
||||
|
{
|
||||
|
obtained_verifier_challenge
|
||||
|
} else {
|
||||
|
return Ok(false);
|
||||
|
};
|
||||
|
end_timer!(verify_time);
|
||||
|
Ok(verifier_challenge == &obtained_verifier_challenge)
|
||||
|
}
|
||||
|
|
||||
|
fn randomize_public_key(
|
||||
|
parameters: &Self::Parameters,
|
||||
|
public_key: &Self::PublicKey,
|
||||
|
randomness: &[u8],
|
||||
|
) -> Result<Self::PublicKey, Error> {
|
||||
|
let rand_pk_time = start_timer!(|| "SchnorrSig::RandomizePubKey");
|
||||
|
|
||||
|
let 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;
|
||||
|
}
|
||||
|
base.double_in_place();
|
||||
|
}
|
||||
|
randomized_pk += &encoded;
|
||||
|
|
||||
|
end_timer!(rand_pk_time);
|
||||
|
|
||||
|
Ok(randomized_pk)
|
||||
|
}
|
||||
|
|
||||
|
fn randomize_signature(
|
||||
|
_parameter: &Self::Parameters,
|
||||
|
signature: &Self::Signature,
|
||||
|
randomness: &[u8],
|
||||
|
) -> Result<Self::Signature, Error> {
|
||||
|
let rand_signature_time = start_timer!(|| "SchnorrSig::RandomizeSig");
|
||||
|
let SchnorrSig {
|
||||
|
prover_response,
|
||||
|
verifier_challenge,
|
||||
|
} = signature;
|
||||
|
let mut base = G::ScalarField::one();
|
||||
|
let mut multiplier = G::ScalarField::zero();
|
||||
|
for bit in bytes_to_bits(randomness) {
|
||||
|
if bit {
|
||||
|
multiplier += &base;
|
||||
|
}
|
||||
|
base.double_in_place();
|
||||
|
}
|
||||
|
|
||||
|
let new_sig = SchnorrSig {
|
||||
|
prover_response: *prover_response - &(*verifier_challenge * &multiplier),
|
||||
|
verifier_challenge: *verifier_challenge,
|
||||
|
};
|
||||
|
end_timer!(rand_signature_time);
|
||||
|
Ok(new_sig)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<bool> {
|
||||
|
let mut bits = Vec::with_capacity(bytes.len() * 8);
|
||||
|
for byte in bytes {
|
||||
|
for i in 0..8 {
|
||||
|
let bit = (*byte >> (8 - i - 1)) & 1;
|
||||
|
bits.push(bit == 1);
|
||||
|
}
|
||||
|
}
|
||||
|
bits
|
||||
|
}
|