Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created CRHF trait and conform RescueCRH impl #169

Merged
merged 8 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ and follow [semantic versioning](https://semver.org/) for our releases.
- New methods for enforcing constraints: `is_member` and `enforce_merkle_proof`.
- Move the remaining methods to the internals of circuit implementation for `RescueMerkleTree`.
- Implement `MerkleTreeGadget` for `RescueMerkleTree`.
- [#169](https://github.com/EspressoSystems/jellyfish/pull/158) (`jf-primitives`) Stabilize API effort
- Introduced `trait CRHF` and moved current implementations under `struct FixedLengthRescueCRHF, VariableLengthRescueCRHF`.
- Introduced `trait CommitmentScheme` and moved current implementations under `struct FixedLengthRescueCommitment`.

### Fixed

Expand All @@ -72,6 +75,7 @@ and follow [semantic versioning](https://semver.org/) for our releases.
- [#105](https://github.com/EspressoSystems/jellyfish/pull/105) (all) Trait bound relaxation
- [#108](https://github.com/EspressoSystems/jellyfish/pull/108) (`jf-utils`) Allowed more general input to `deserialize_canonical_bytes!()`
- [#113](https://github.com/EspressoSystems/jellyfish/pull/113) (`jf-plonk`) Corrected error type for `PlonkVerifier` gadgets
- [#162](https://github.com/EspressoSystems/jellyfish/pull/162) (`jf-utils`) Renamed `#serde(with="field_elem")` to `#serde(with="canonical")`

### Removed

Expand Down
8 changes: 4 additions & 4 deletions plonk/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub enum PlonkError {
/// Plonk proof verification failed due to wrong proof
WrongProof,
/// Rescue Error
RescueError(jf_primitives::rescue::errors::RescueError),
PrimitiveError(jf_primitives::errors::PrimitivesError),
/// Invalid parameters
InvalidParameters(String),
/// Non-native field overflow
Expand Down Expand Up @@ -66,9 +66,9 @@ impl From<ark_serialize::SerializationError> for PlonkError {
}
}

impl From<jf_primitives::rescue::errors::RescueError> for PlonkError {
fn from(e: jf_primitives::rescue::errors::RescueError) -> Self {
Self::RescueError(e)
impl From<jf_primitives::errors::PrimitivesError> for PlonkError {
fn from(e: jf_primitives::errors::PrimitivesError) -> Self {
Self::PrimitiveError(e)
}
}

Expand Down
2 changes: 1 addition & 1 deletion plonk/src/transcript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,5 @@ pub trait PlonkTranscript<F> {
/// and then append it to the transcript.
fn get_and_append_challenge<E>(&mut self, label: &'static [u8]) -> Result<E::Fr, PlonkError>
where
E: PairingEngine;
E: PairingEngine<Fq = F>;
}
7 changes: 4 additions & 3 deletions plonk/src/transcript/rescue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use ark_ec::{
};
use ark_std::vec::Vec;
use jf_primitives::{
crhf::{VariableLengthRescueCRHF, CRHF},
pcs::prelude::Commitment,
rescue::{sponge::RescueCRHF, RescueParameter, STATE_SIZE},
rescue::{RescueParameter, STATE_SIZE},
};
use jf_relation::gadgets::ecc::{Point, SWToTEConParam};
use jf_utils::{bytes_to_field_elements, field_switching, fq_to_fr_with_mask};
Expand Down Expand Up @@ -169,14 +170,14 @@ where
/// efficiency.
fn get_and_append_challenge<E>(&mut self, _label: &'static [u8]) -> Result<E::Fr, PlonkError>
where
E: PairingEngine,
E: PairingEngine<Fq = F>,
{
// 1. state: [F: STATE_SIZE] = hash(state|transcript)
// 2. challenge = state[0] in Fr
// 3. transcript = Vec::new()

let input = [self.state.as_ref(), self.transcript.as_ref()].concat();
let tmp = RescueCRHF::sponge_with_padding(&input, STATE_SIZE);
let tmp: [F; STATE_SIZE] = VariableLengthRescueCRHF::evaluate(&input)?;
let challenge = fq_to_fr_with_mask::<F, E::Fr>(&tmp[0]);
self.state.copy_from_slice(&tmp);
self.transcript = Vec::new();
Expand Down
26 changes: 19 additions & 7 deletions primitives/src/circuit/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ where

#[cfg(test)]
mod tests {
use crate::{circuit::commitment::CommitmentGadget, commitment::Commitment};
use crate::{
circuit::commitment::CommitmentGadget,
commitment::{CommitmentScheme, FixedLengthRescueCommitment},
};
use ark_bls12_377::Fq as Fq377;
use ark_ed_on_bls12_377::Fq as FqEd377;
use ark_ed_on_bls12_381::Fq as FqEd381;
Expand All @@ -52,23 +55,32 @@ mod tests {
use itertools::Itertools;
use jf_relation::{Circuit, PlonkCircuit, Variable};

const TEST_INPUT_LEN: usize = 10;
const TEST_INPUT_LEN_PLUS_ONE: usize = 10 + 1;

macro_rules! test_commit_circuit {
($base_field:tt) => {
let mut circuit: PlonkCircuit<$base_field> = PlonkCircuit::new_turbo_plonk();
let mut prng = ark_std::test_rng();

let blinding = $base_field::rand(&mut prng);
let blinding_var = circuit.create_variable(blinding).unwrap();
let input_len = 10;
let data: Vec<$base_field> = (0..input_len)
.map(|_| $base_field::rand(&mut prng))
.collect_vec();

let mut data = [$base_field::from(0u64); TEST_INPUT_LEN];
for i in 0..TEST_INPUT_LEN {
data[i] = $base_field::rand(&mut prng);
}
let data_vars: Vec<Variable> = data
.iter()
.map(|&x| circuit.create_variable(x).unwrap())
.collect_vec();

let commitment_instance = Commitment::new(input_len);
let expected_commitment = commitment_instance.commit(&data, &blinding).unwrap();
let expected_commitment = FixedLengthRescueCommitment::<
$base_field,
TEST_INPUT_LEN,
TEST_INPUT_LEN_PLUS_ONE,
>::commit(&data, Some(&blinding))
.unwrap();

let commitment_var = circuit.commit(&data_vars, blinding_var).unwrap();

Expand Down
2 changes: 1 addition & 1 deletion primitives/src/circuit/merkle_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ mod rescue_merkle_tree;
/// let (_, proof) = mt.lookup(2).expect_ok().unwrap();
///
/// // Circuit computation with a MT
/// let leaf_var = circuit.create_leaf_variable(Fq::from(2_u64), Fq::from(100_u64)).unwrap();
/// let leaf_var = circuit.create_leaf_variable(2_u64, Fq::from(100_u64)).unwrap();
/// let path_vars = circuit.create_membership_proof_variable(&proof).unwrap();
/// let root_var = circuit.create_root_variable(expected_root).unwrap();
/// circuit.enforce_merkle_proof(leaf_var, path_vars, root_var).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion primitives/src/circuit/rescue/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@ mod tests {
}

// Check consistency between outputs
let expected_hash = RescueCRHF::sponge_with_padding(&input_vec, output_len);
let expected_hash = RescueCRHF::sponge_with_bit_padding(&input_vec, output_len);

for (&e, &f) in expected_hash.iter().zip(out_var.iter()) {
assert_eq!(e, circuit.witness(f).unwrap());
Expand Down
4 changes: 2 additions & 2 deletions primitives/src/circuit/rescue/non_native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ mod tests {
.map(|x| FpElemVar::new_from_field_element(&mut circuit, x, m, None).unwrap())
.collect();

let expected_sponge = RescueCRHF::sponge_with_padding(&data_t, 1);
let expected_sponge = RescueCRHF::sponge_with_bit_padding(&data_t, 1);

// sponge with padding
let sponge_var = RescueNonNativeGadget::<T, F>::rescue_sponge_with_padding(
Expand All @@ -1097,7 +1097,7 @@ mod tests {

// sponge full with padding
for output_len in 1..max_output_len {
let expected_sponge = RescueCRHF::sponge_with_padding(&data_t, output_len);
let expected_sponge = RescueCRHF::sponge_with_bit_padding(&data_t, output_len);

let sponge_var = RescueNonNativeGadget::<T, F>::rescue_sponge_with_padding(
&mut circuit,
Expand Down
168 changes: 105 additions & 63 deletions primitives/src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,83 @@
use ark_std::marker::PhantomData;

use crate::{
crhf::{FixedLengthRescueCRHF, CRHF},
errors::PrimitivesError,
rescue::{sponge::RescueCRHF, RescueParameter, CRHF_RATE},
rescue::RescueParameter,
};
use ark_std::{format, string::String, vec};
use jf_utils::pad_with_zeros;

#[derive(Default)]
/// Commitment instance for user defined input size (in scalar elements)
pub struct Commitment<F: RescueParameter> {
input_len: usize,
phantom_f: PhantomData<F>,
use ark_std::{
borrow::Borrow,
fmt::Debug,
hash::Hash,
string::{String, ToString},
UniformRand,
};

/// A trait for cryptographic commitment scheme
pub trait CommitmentScheme {
/// Input to the commitment
type Input;
/// The type of output commitment value
type Output: Clone + Debug + PartialEq + Eq + Hash;
/// The type of the hiding/blinding factor
type Randomness: Clone + Debug + PartialEq + Eq + UniformRand;

/// Commit algorithm that takes `input` and blinding randomness `r`
/// (optional for hiding commitment schemes), outputs a commitment.
fn commit<T: Borrow<Self::Input>>(
input: T,
r: Option<&Self::Randomness>,
) -> Result<Self::Output, PrimitivesError>;

/// Verify algorithm that output `Ok` if accepted, or `Err` if rejected.
fn verify<T: Borrow<Self::Input>>(
input: T,
r: Option<&Self::Randomness>,
comm: &Self::Output,
) -> Result<(), PrimitivesError>;
}

impl<F: RescueParameter> Commitment<F> {
/// Create a new commitment instance for inputs of length `input_len`
pub fn new(input_len: usize) -> Commitment<F> {
assert!(input_len > 0, "input_len must be positive");
Commitment {
input_len,
phantom_f: PhantomData,
}
}
/// Commits to `input` slice using blinding `blind`. Return
/// Err(PrimitivesError::ParameterError) if input.len() !=
/// self.input_len
pub fn commit(&self, input: &[F], blind: &F) -> Result<F, PrimitivesError> {
if input.len() != self.input_len {
return Err(PrimitivesError::ParameterError(format!(
"Commitment error: expected input length ({}). It must match \
instance's message length ({})",
self.input_len,
input.len(),
)));
}
let mut msg = vec![*blind];
msg.extend_from_slice(input);
// Ok to pad with 0's since input length is fixed for the commitment instance
pad_with_zeros(&mut msg, CRHF_RATE);
let result_vec = RescueCRHF::sponge_no_padding(msg.as_slice(), 1)?;
Ok(result_vec[0])
#[derive(Debug, Default, Clone, PartialEq, Eq)]
/// Rescue-based Commitment instance for fixed-length input
///
/// ## Note
/// the current ugly existence of `INPUT_LEN_PLUS_ONE` is due to unstable
/// feature of using const generic in expression (namely can't use `INPUT_LEN +
/// 1` in code).
// FIXME: (alex) when `feature(generic_const_exprs)` is stable, we should remove
// the third generic param. See more: https://github.com/rust-lang/rust/issues/76560
pub struct FixedLengthRescueCommitment<
F: RescueParameter,
const INPUT_LEN: usize,
const INPUT_LEN_PLUS_ONE: usize,
>(PhantomData<F>);

impl<F: RescueParameter, const INPUT_LEN: usize, const INPUT_LEN_PLUS_ONE: usize> CommitmentScheme
for FixedLengthRescueCommitment<F, INPUT_LEN, INPUT_LEN_PLUS_ONE>
{
type Input = [F; INPUT_LEN];
type Output = F;
type Randomness = F;

fn commit<T: Borrow<Self::Input>>(
input: T,
r: Option<&Self::Randomness>,
) -> Result<Self::Output, PrimitivesError> {
let mut msg = [F::zero(); INPUT_LEN_PLUS_ONE];
msg[0] = *r.ok_or_else(|| {
PrimitivesError::ParameterError("Expecting a blinding factor".to_string())
})?;
msg[1..INPUT_LEN_PLUS_ONE].copy_from_slice(&input.borrow()[..(INPUT_LEN)]);

Ok(FixedLengthRescueCRHF::<F, INPUT_LEN_PLUS_ONE, 1>::evaluate(&msg)?[0])
}
/// Verifies `commitment` against `input` and `blind`.
/// Returns Ok(()) on success. Otherwise, returns
/// PrimitivesError::ParameterError if input.len() != self.input_len,
/// and PrimitivesError::VerificationError if commitment is not valid.
pub fn verify(&self, input: &[F], blind: &F, commitment: &F) -> Result<(), PrimitivesError> {
if self.commit(input, blind)? == *commitment {

fn verify<T: Borrow<Self::Input>>(
input: T,
r: Option<&Self::Randomness>,
comm: &Self::Output,
) -> Result<(), PrimitivesError> {
if <Self as CommitmentScheme>::commit(input, r)? == *comm {
Ok(())
} else {
Err(PrimitivesError::VerificationError(String::from(
Expand All @@ -67,44 +97,56 @@ impl<F: RescueParameter> Commitment<F> {

#[cfg(test)]
mod test {
use crate::commitment::Commitment;
use crate::{
commitment::{CommitmentScheme, FixedLengthRescueCommitment},
rescue::{sponge::RescueCRHF, CRHF_RATE},
};
use ark_bls12_377::Fq as Fq377;
use ark_ed_on_bls12_377::Fq as FqEd377;
use ark_ed_on_bls12_381::Fq as FqEd381;
use ark_ed_on_bls12_381_bandersnatch::Fq as FqEd381b;
use ark_ed_on_bn254::Fq as FqEd254;
use ark_ff::UniformRand;
use core::ops::Add;
use ark_std::vec;

macro_rules! test_commit {
($tr:tt) => {
let mut prng = ark_std::test_rng();
let commitment = Commitment::new(3);

let input = [$tr::from(1u64), $tr::from(2u64), $tr::from(3u64)];
let blind = $tr::rand(&mut prng);
let c = commitment.commit(&input, &blind).unwrap();
assert!(commitment.verify(&input, &blind, &c).is_ok());

let c = FixedLengthRescueCommitment::<$tr, 3, 4>::commit(&input, Some(&blind)).unwrap();
assert!(
FixedLengthRescueCommitment::<$tr, 3, 4>::verify(&input, Some(&blind), &c).is_ok()
);
// test for correctness
let mut msg = vec![blind];
msg.extend_from_slice(&input);
if (input.len() + 1) % CRHF_RATE == 0 {
assert_eq!(c, RescueCRHF::sponge_no_padding(&msg, 1).unwrap()[0])
} else {
assert_eq!(c, RescueCRHF::sponge_with_zero_padding(&msg, 1)[0])
}

// smaller input size
assert!(commitment.verify(&input[0..2], &blind, &c).is_err());
let bad_input = [input[0], input[1]];
assert!(
FixedLengthRescueCommitment::<$tr, 2, 3>::verify(&bad_input, Some(&blind), &c)
.is_err()
);
// bad blinding factor
let bad_blind = blind.add($tr::from(1u8));
assert!(commitment.verify(&input, &bad_blind, &c).is_err());
let bad_blind = blind + $tr::from(1u8);
assert!(
FixedLengthRescueCommitment::<$tr, 3, 4>::verify(&input, Some(&bad_blind), &c)
.is_err()
);
// bad input
let bad_input = [$tr::from(2u64), $tr::from(1u64), $tr::from(3u64)];
assert!(commitment.verify(&bad_input, &blind, &c).is_err());

// smaller input size
let bad_size_input = [$tr::from(2u64), $tr::from(1u64)];
assert!(commitment.commit(&bad_size_input, &blind).is_err());
// greater input size
let bad_size_input = [
$tr::from(2u64),
$tr::from(1u64),
$tr::from(1u64),
$tr::from(1u64),
];
assert!(commitment.commit(&bad_size_input, &blind).is_err());
assert!(
FixedLengthRescueCommitment::<$tr, 3, 4>::verify(&bad_input, Some(&blind), &c)
.is_err()
);
};
}

Expand Down
Loading