diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6bfc5215db104ff41cc216d638cc600e8c64450..9203dcd4bb97549777fac6e0a4baf5be728076ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,13 @@
 
 ### Breaking changes
 
+- [\#82](https://github.com/arkworks-rs/poly-commit/pull/82) Function parameter `opening_challenge: F` for `open`,
+  `check`,  has been changed from `F` to `opening_challenges: &mut ChallengeGenerator`.
+
 ### Features
 
+- [\#82](https://github.com/arkworks-rs/poly-commit/pull/82) Add multivariate opening challenge strategy. Integrate with sponge API. 
+
 ### Improvements
 
 ### Bug fixes
diff --git a/Cargo.toml b/Cargo.toml
index e606cd1bd97ce0279298b05f4f603c576295006d..b4805c126b7564df2651dae51f82488233998eee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ ark-serialize = { version = "^0.3.0", default-features = false, features = [ "de
 ark-ff = { version = "^0.3.0", default-features = false }
 ark-ec = { version = "^0.3.0", default-features = false }
 ark-poly = {version = "^0.3.0", default-features = false }
+ark-sponge = {version = "^0.3.0", default-features = false}
 
 ark-std = { version = "^0.3.0", default-features = false }
 ark-relations = { version = "^0.3.0", default-features = false, optional = true }
@@ -36,8 +37,6 @@ digest = "0.9"
 rayon = { version = "1", optional = true }
 derivative = { version = "2", features = [ "use_core" ] }
 
-tracing = { version = "0.1", default-features = false, features = [ "attributes" ] }
-
 [dev-dependencies]
 ark-ed-on-bls12-381 = { version = "^0.3.0", default-features = false }
 ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] }
@@ -58,7 +57,7 @@ debug = true
 
 [features]
 default = [ "std", "parallel" ]
-std = [ "ark-ff/std", "ark-ec/std", "ark-nonnative-field/std", "ark-poly/std", "ark-std/std", "ark-relations/std", "ark-serialize/std" ]
-r1cs = [ "ark-relations", "ark-r1cs-std", "ark-nonnative-field", "hashbrown" ]
+std = [ "ark-ff/std", "ark-ec/std", "ark-nonnative-field/std", "ark-poly/std", "ark-std/std", "ark-relations/std", "ark-serialize/std", "ark-sponge/std"]
+r1cs = [ "ark-relations", "ark-r1cs-std", "ark-nonnative-field", "hashbrown", "ark-sponge/r1cs"]
 print-trace = [ "ark-std/print-trace" ]
 parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-poly/parallel", "ark-std/parallel", "rayon" ]
diff --git a/src/challenge.rs b/src/challenge.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aa78d9028248388da3b54e4840f2c276070e08d1
--- /dev/null
+++ b/src/challenge.rs
@@ -0,0 +1,61 @@
+use ark_ff::PrimeField;
+use ark_sponge::{CryptographicSponge, FieldElementSize};
+
+/// `ChallengeGenerator` generates opening challenges using multivariate or univariate strategy.
+/// For multivariate strategy, each challenge is freshly squeezed from a sponge.
+/// For univariate strategy, each challenge is a power of one squeezed element from sponge.
+///
+/// Note that mutable reference cannot be cloned.
+#[derive(Clone)]
+pub enum ChallengeGenerator<F: PrimeField, S: CryptographicSponge> {
+    /// Each challenge is freshly squeezed from a sponge.
+    Multivariate(S),
+    /// Each challenge is a power of one squeezed element from sponge.
+    ///
+    /// `Univariate(generator, next_element)`
+    Univariate(F, F),
+}
+
+impl<F: PrimeField, S: CryptographicSponge> ChallengeGenerator<F, S> {
+    /// Returns a challenge generator with multivariate strategy. Each challenge is freshly squeezed
+    /// from a sponge.
+    pub fn new_multivariate(sponge: S) -> Self {
+        Self::Multivariate(sponge)
+    }
+
+    /// Returns a challenge generator with univariate strategy. Each challenge is a power of one
+    /// squeezed element from sponge.
+    pub fn new_univariate(sponge: &mut S) -> Self {
+        let gen = sponge.squeeze_field_elements(1)[0];
+        Self::Univariate(gen, gen)
+    }
+
+    /// Returns a challenge of size `size`.
+    /// * If `self == Self::Multivariate(...)`, then this squeezes out a challenge of size `size`.
+    /// * If `self == Self::Univariate(...)`, then this ignores the `size` argument and simply squeezes out
+    /// the next field element.
+    pub fn try_next_challenge_of_size(&mut self, size: FieldElementSize) -> F {
+        match self {
+            // multivariate (full)
+            Self::Multivariate(sponge) => sponge.squeeze_field_elements_with_sizes(&[size])[0],
+            // univariate
+            Self::Univariate(gen, next) => {
+                let result = next.clone();
+                *next *= *gen;
+                result
+            }
+        }
+    }
+    /// Returns the next challenge generated.
+    pub fn next_challenge(&mut self) -> F {
+        self.try_next_challenge_of_size(FieldElementSize::Full)
+    }
+
+    /// Returns the sponge state if `self` is multivariate. Returns `None` otherwise.
+    pub fn into_sponge(self) -> Option<S> {
+        match self {
+            Self::Multivariate(s) => Some(s),
+            _ => None,
+        }
+    }
+}
diff --git a/src/constraints.rs b/src/constraints.rs
index 41fe3582c9ac4657a0869eea0936e580ced32e58..5576d3e100f591bcc586248a2ec9a2e53e85266d 100644
--- a/src/constraints.rs
+++ b/src/constraints.rs
@@ -7,6 +7,7 @@ use ark_nonnative_field::NonNativeFieldVar;
 use ark_poly::Polynomial;
 use ark_r1cs_std::{fields::fp::FpVar, prelude::*};
 use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError};
+use ark_sponge::CryptographicSponge;
 use ark_std::{borrow::Borrow, cmp::Eq, cmp::PartialEq, hash::Hash, marker::Sized};
 use hashbrown::{HashMap, HashSet};
 
@@ -93,8 +94,9 @@ pub struct PCCheckRandomDataVar<TargetField: PrimeField, BaseField: PrimeField>
 pub trait PCCheckVar<
     PCF: PrimeField,
     P: Polynomial<PCF>,
-    PC: PolynomialCommitment<PCF, P>,
+    PC: PolynomialCommitment<PCF, P, S>,
     ConstraintF: PrimeField,
+    S: CryptographicSponge,
 >: Clone
 {
     /// An allocated version of `PC::VerifierKey`.
@@ -117,7 +119,7 @@ pub trait PCCheckVar<
     type ProofVar: AllocVar<PC::Proof, ConstraintF> + Clone;
 
     /// An allocated version of `PC::BatchLCProof`.
-    type BatchLCProofVar: AllocVar<BatchLCProof<PCF, P, PC>, ConstraintF> + Clone;
+    type BatchLCProofVar: AllocVar<BatchLCProof<PCF, PC::BatchProof>, ConstraintF> + Clone;
 
     /// Add to `ConstraintSystemRef<ConstraintF>` new constraints that check that `proof_i` is a valid evaluation
     /// proof at `point_i` for the polynomial in `commitment_i`.
diff --git a/src/data_structures.rs b/src/data_structures.rs
index e07c2e6aeb80609b41d1ec222fb9350537bbf086..ccf758746b1c39ca397d417577934584316b9094 100644
--- a/src/data_structures.rs
+++ b/src/data_structures.rs
@@ -1,5 +1,5 @@
-use crate::{Polynomial, PolynomialCommitment, Rc, String, Vec};
-use ark_ff::{Field, ToConstraintField};
+use crate::{Polynomial, Rc, String, Vec};
+use ark_ff::{Field, PrimeField, ToConstraintField};
 use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError};
 use ark_std::rand::RngCore;
 use ark_std::{
@@ -104,9 +104,9 @@ pub trait PCProof: Clone + ark_ff::ToBytes + CanonicalSerialize + CanonicalDeser
 
 /// A proof of satisfaction of linear combinations.
 #[derive(Clone, CanonicalSerialize, CanonicalDeserialize)]
-pub struct BatchLCProof<F: Field, P: Polynomial<F>, PC: PolynomialCommitment<F, P>> {
+pub struct BatchLCProof<F: PrimeField, T: Clone + CanonicalSerialize + CanonicalDeserialize> {
     /// Evaluation proof.
-    pub proof: PC::BatchProof,
+    pub proof: T,
     /// Evaluations required to verify the proof.
     pub evals: Option<Vec<F>>,
 }
diff --git a/src/ipa_pc/mod.rs b/src/ipa_pc/mod.rs
index 03ca3f8c724259128465ba42233fe0d49f190304..fb9cc38596d56ba241017c4aca3884aa19bebb33 100644
--- a/src/ipa_pc/mod.rs
+++ b/src/ipa_pc/mod.rs
@@ -1,4 +1,4 @@
-use crate::{BTreeMap, BTreeSet, String, ToString, Vec};
+use crate::{BTreeMap, BTreeSet, String, ToString, Vec, CHALLENGE_SIZE};
 use crate::{BatchLCProof, Error, Evaluations, QuerySet, UVPolynomial};
 use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination};
 use crate::{PCCommitterKey, PCRandomness, PCUniversalParams, PolynomialCommitment};
@@ -14,6 +14,8 @@ pub use data_structures::*;
 #[cfg(feature = "parallel")]
 use rayon::prelude::*;
 
+use crate::challenge::ChallengeGenerator;
+use ark_sponge::CryptographicSponge;
 use digest::Digest;
 
 /// A polynomial commitment scheme based on the hardness of the
@@ -29,13 +31,25 @@ use digest::Digest;
 ///
 /// [pcdas]: https://eprint.iacr.org/2020/499
 /// [marlin]: https://eprint.iacr.org/2019/1047
-pub struct InnerProductArgPC<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> {
+pub struct InnerProductArgPC<
+    G: AffineCurve,
+    D: Digest,
+    P: UVPolynomial<G::ScalarField>,
+    S: CryptographicSponge,
+> {
     _projective: PhantomData<G>,
     _digest: PhantomData<D>,
     _poly: PhantomData<P>,
+    _sponge: PhantomData<S>,
 }
 
-impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArgPC<G, D, P> {
+impl<G, D, P, S> InnerProductArgPC<G, D, P, S>
+where
+    G: AffineCurve,
+    D: Digest,
+    P: UVPolynomial<G::ScalarField>,
+    S: CryptographicSponge,
+{
     /// `PROTOCOL_NAME` is used as a seed for the setup function.
     pub const PROTOCOL_NAME: &'static [u8] = b"PC-DL-2020";
 
@@ -88,7 +102,7 @@ impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArg
         point: G::ScalarField,
         values: impl IntoIterator<Item = G::ScalarField>,
         proof: &Proof<G>,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
     ) -> Option<SuccinctCheckPolynomial<G::ScalarField>> {
         let check_time = start_timer!(|| "Succinct checking");
 
@@ -100,9 +114,7 @@ impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArg
         let mut combined_commitment_proj = G::Projective::zero();
         let mut combined_v = G::ScalarField::zero();
 
-        let mut opening_challenge_counter = 0;
-        let mut cur_challenge = opening_challenges(opening_challenge_counter);
-        opening_challenge_counter += 1;
+        let mut cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
         let labeled_commitments = commitments.into_iter();
         let values = values.into_iter();
@@ -111,8 +123,7 @@ impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArg
             let commitment = labeled_commitment.commitment();
             combined_v += &(cur_challenge * &value);
             combined_commitment_proj += &labeled_commitment.commitment().comm.mul(cur_challenge);
-            cur_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
             let degree_bound = labeled_commitment.degree_bound();
             assert_eq!(degree_bound.is_some(), commitment.shifted_comm.is_some());
@@ -123,8 +134,7 @@ impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArg
                 combined_commitment_proj += &commitment.shifted_comm.unwrap().mul(cur_challenge);
             }
 
-            cur_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
         }
 
         let mut combined_commitment = combined_commitment_proj.into_affine();
@@ -302,11 +312,12 @@ impl<G: AffineCurve, D: Digest, P: UVPolynomial<G::ScalarField>> InnerProductArg
     }
 }
 
-impl<G, D, P> PolynomialCommitment<G::ScalarField, P> for InnerProductArgPC<G, D, P>
+impl<G, D, P, S> PolynomialCommitment<G::ScalarField, P, S> for InnerProductArgPC<G, D, P, S>
 where
     G: AffineCurve,
     D: Digest,
     P: UVPolynomial<G::ScalarField, Point = G::ScalarField>,
+    S: CryptographicSponge,
 {
     type UniversalParams = UniversalParams<G>;
     type CommitterKey = CommitterKey<G>;
@@ -450,12 +461,12 @@ where
         Ok((comms, rands))
     }
 
-    fn open_individual_opening_challenges<'a>(
+    fn open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<G::ScalarField, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::Proof, Self::Error>
@@ -476,9 +487,7 @@ where
 
         let combine_time = start_timer!(|| "Combining polynomials, randomness, and commitments.");
 
-        let mut opening_challenge_counter = 0;
-        let mut cur_challenge = opening_challenges(opening_challenge_counter);
-        opening_challenge_counter += 1;
+        let mut cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
         for (labeled_polynomial, (labeled_commitment, randomness)) in
             polys_iter.zip(comms_iter.zip(rands_iter))
@@ -500,8 +509,7 @@ where
                 combined_rand += &(cur_challenge * &randomness.rand);
             }
 
-            cur_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
             let has_degree_bound = degree_bound.is_some();
 
@@ -534,8 +542,7 @@ where
                 }
             }
 
-            cur_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            cur_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
         }
 
         end_timer!(combine_time);
@@ -694,13 +701,13 @@ where
         })
     }
 
-    fn check_individual_opening_challenges<'a>(
+    fn check<'a>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
         values: impl IntoIterator<Item = G::ScalarField>,
         proof: &Self::Proof,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<bool, Self::Error>
     where
@@ -745,13 +752,13 @@ where
         Ok(true)
     }
 
-    fn batch_check_individual_opening_challenges<'a, R: RngCore>(
+    fn batch_check<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         values: &Evaluations<G::ScalarField, P::Point>,
         proof: &Self::BatchProof,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -831,16 +838,16 @@ where
         Ok(true)
     }
 
-    fn open_combinations_individual_opening_challenges<'a>(
+    fn open_combinations<'a>(
         ck: &Self::CommitterKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<G::ScalarField, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<G::ScalarField, P, Self>, Self::Error>
+    ) -> Result<BatchLCProof<G::ScalarField, Self::BatchProof>, Self::Error>
     where
         Self::Randomness: 'a,
         Self::Commitment: 'a,
@@ -858,7 +865,7 @@ where
         let mut lc_commitments = Vec::new();
         let mut lc_info = Vec::new();
 
-        for lc in lc_s {
+        for lc in linear_combinations {
             let lc_label = lc.label().clone();
             let mut poly = P::zero();
             let mut degree_bound = None;
@@ -927,7 +934,7 @@ where
 
         let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments);
 
-        let proof = Self::batch_open_individual_opening_challenges(
+        let proof = Self::batch_open(
             ck,
             lc_polynomials.iter(),
             lc_commitments.iter(),
@@ -941,14 +948,14 @@ where
 
     /// Checks that `values` are the true evaluations at `query_set` of the polynomials
     /// committed in `labeled_commitments`.
-    fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
+    fn check_combinations<'a, R: RngCore>(
         vk: &Self::VerifierKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<G::ScalarField>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<G::ScalarField>,
-        evaluations: &Evaluations<G::ScalarField, P::Point>,
-        proof: &BatchLCProof<G::ScalarField, P, Self>,
-        opening_challenges: &dyn Fn(u64) -> G::ScalarField,
+        eqn_query_set: &QuerySet<P::Point>,
+        eqn_evaluations: &Evaluations<P::Point, G::ScalarField>,
+        proof: &BatchLCProof<G::ScalarField, Self::BatchProof>,
+        opening_challenges: &mut ChallengeGenerator<G::ScalarField, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -962,8 +969,8 @@ where
 
         let mut lc_commitments = Vec::new();
         let mut lc_info = Vec::new();
-        let mut evaluations = evaluations.clone();
-        for lc in lc_s {
+        let mut evaluations = eqn_evaluations.clone();
+        for lc in linear_combinations {
             let lc_label = lc.label().clone();
             let num_polys = lc.len();
 
@@ -1015,10 +1022,10 @@ where
 
         let lc_commitments = Self::construct_labeled_commitments(&lc_info, &lc_commitments);
 
-        Self::batch_check_individual_opening_challenges(
+        Self::batch_check(
             vk,
             &lc_commitments,
-            &query_set,
+            &eqn_query_set,
             &evaluations,
             proof,
             opening_challenges,
@@ -1032,15 +1039,18 @@ mod tests {
     #![allow(non_camel_case_types)]
 
     use super::InnerProductArgPC;
+    use ark_ec::AffineCurve;
     use ark_ed_on_bls12_381::{EdwardsAffine, Fr};
     use ark_ff::PrimeField;
     use ark_poly::{univariate::DensePolynomial as DensePoly, UVPolynomial};
+    use ark_sponge::poseidon::PoseidonSponge;
     use ark_std::rand::rngs::StdRng;
     use blake2::Blake2s;
 
     type UniPoly = DensePoly<Fr>;
-    type PC<E, D, P> = InnerProductArgPC<E, D, P>;
-    type PC_JJB2S = PC<EdwardsAffine, Blake2s, UniPoly>;
+    type Sponge = PoseidonSponge<<EdwardsAffine as AffineCurve>::ScalarField>;
+    type PC<E, D, P, S> = InnerProductArgPC<E, D, P, S>;
+    type PC_JJB2S = PC<EdwardsAffine, Blake2s, UniPoly, Sponge>;
 
     fn rand_poly<F: PrimeField>(degree: usize, _: Option<usize>, rng: &mut StdRng) -> DensePoly<F> {
         DensePoly::rand(degree, rng)
@@ -1057,23 +1067,34 @@ mod tests {
     #[test]
     fn single_poly_test() {
         use crate::tests::*;
-        single_poly_test::<_, _, PC_JJB2S>(None, rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        single_poly_test::<_, _, PC_JJB2S, _>(
+            None,
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn constant_poly_test() {
         use crate::tests::*;
-        single_poly_test::<_, _, PC_JJB2S>(None, constant_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        single_poly_test::<_, _, PC_JJB2S, _>(
+            None,
+            constant_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn quadratic_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S>(
+        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S, _>(
             rand_poly::<Fr>,
             rand_point::<Fr>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for ed_on_bls12_381-blake2s");
     }
@@ -1081,23 +1102,32 @@ mod tests {
     #[test]
     fn linear_poly_degree_bound_test() {
         use crate::tests::*;
-        linear_poly_degree_bound_test::<_, _, PC_JJB2S>(rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        linear_poly_degree_bound_test::<_, _, PC_JJB2S, _>(
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn single_poly_degree_bound_test() {
         use crate::tests::*;
-        single_poly_degree_bound_test::<_, _, PC_JJB2S>(rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        single_poly_degree_bound_test::<_, _, PC_JJB2S, _>(
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn single_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        single_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S>(
+        single_poly_degree_bound_multiple_queries_test::<_, _, PC_JJB2S, _>(
             rand_poly::<Fr>,
             rand_point::<Fr>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for ed_on_bls12_381-blake2s");
     }
@@ -1105,9 +1135,10 @@ mod tests {
     #[test]
     fn two_polys_degree_bound_single_query_test() {
         use crate::tests::*;
-        two_polys_degree_bound_single_query_test::<_, _, PC_JJB2S>(
+        two_polys_degree_bound_single_query_test::<_, _, PC_JJB2S, _>(
             rand_poly::<Fr>,
             rand_point::<Fr>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for ed_on_bls12_381-blake2s");
     }
@@ -1115,40 +1146,64 @@ mod tests {
     #[test]
     fn full_end_to_end_test() {
         use crate::tests::*;
-        full_end_to_end_test::<_, _, PC_JJB2S>(None, rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        full_end_to_end_test::<_, _, PC_JJB2S, _>(
+            None,
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn single_equation_test() {
         use crate::tests::*;
-        single_equation_test::<_, _, PC_JJB2S>(None, rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        single_equation_test::<_, _, PC_JJB2S, _>(
+            None,
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn two_equation_test() {
         use crate::tests::*;
-        two_equation_test::<_, _, PC_JJB2S>(None, rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        two_equation_test::<_, _, PC_JJB2S, _>(
+            None,
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn two_equation_degree_bound_test() {
         use crate::tests::*;
-        two_equation_degree_bound_test::<_, _, PC_JJB2S>(rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        two_equation_degree_bound_test::<_, _, PC_JJB2S, _>(
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 
     #[test]
     fn full_end_to_end_equation_test() {
         use crate::tests::*;
-        full_end_to_end_equation_test::<_, _, PC_JJB2S>(None, rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        full_end_to_end_equation_test::<_, _, PC_JJB2S, _>(
+            None,
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 
@@ -1156,8 +1211,12 @@ mod tests {
     #[should_panic]
     fn bad_degree_bound_test() {
         use crate::tests::*;
-        bad_degree_bound_test::<_, _, PC_JJB2S>(rand_poly::<Fr>, rand_point::<Fr>)
-            .expect("test failed for ed_on_bls12_381-blake2s");
+        bad_degree_bound_test::<_, _, PC_JJB2S, _>(
+            rand_poly::<Fr>,
+            rand_point::<Fr>,
+            poseidon_sponge_for_test,
+        )
+        .expect("test failed for ed_on_bls12_381-blake2s");
         println!("Finished ed_on_bls12_381-blake2s");
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 2f8fcf92aa055285d6d5d6395e9a2e120627a2ad..0107c1e9c0a9d6daddc508085e9c8dadf8b83ef4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,12 +10,13 @@
 #![deny(unused_comparisons, bare_trait_objects, unused_must_use, const_err)]
 #![forbid(unsafe_code)]
 
+#[allow(unused)]
 #[macro_use]
 extern crate derivative;
 #[macro_use]
 extern crate ark_std;
 
-use ark_ff::Field;
+use ark_ff::{Field, PrimeField};
 pub use ark_poly::{Polynomial, UVPolynomial};
 use ark_std::rand::RngCore;
 
@@ -97,6 +98,8 @@ pub mod sonic_pc;
 /// [pcdas]: https://eprint.iacr.org/2020/499
 pub mod ipa_pc;
 
+/// Defines the challenge strategies and challenge generator.
+pub mod challenge;
 /// A multilinear polynomial commitment scheme that converts n-variate multilinear polynomial into
 /// n quotient UV polynomial. This scheme is based on hardness of the discrete logarithm
 /// in prime-order groups. Construction is detailed in [[XZZPD19]][xzzpd19] and [[ZGKPP18]][zgkpp18]
@@ -105,6 +108,8 @@ pub mod ipa_pc;
 /// [zgkpp]: https://ieeexplore.ieee.org/document/8418645
 pub mod multilinear_pc;
 
+use crate::challenge::ChallengeGenerator;
+use ark_sponge::{CryptographicSponge, FieldElementSize};
 /// Multivariate polynomial commitment based on the construction in
 /// [[PST13]][pst] with batching and (optional) hiding property inspired
 /// by the univariate scheme in [[CHMMVW20, "Marlin"]][marlin]
@@ -130,7 +135,9 @@ pub type Evaluations<T, F> = BTreeMap<(String, T), F>;
 /// a sender to commit to multiple polynomials and later provide a succinct proof
 /// of evaluation for the corresponding commitments at a query set `Q`, while
 /// enforcing per-polynomial degree bounds.
-pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
+pub trait PolynomialCommitment<F: PrimeField, P: Polynomial<F>, S: CryptographicSponge>:
+    Sized
+{
     /// The universal parameters for the commitment scheme. These are "trimmed"
     /// down to `Self::CommitterKey` and `Self::VerifierKey` by `Self::trim`.
     type UniversalParams: PCUniversalParams;
@@ -199,180 +206,13 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
     where
         P: 'a;
 
-    /// On input a list of labeled polynomials and a query point, `open` outputs a proof of evaluation
-    /// of the polynomials at the query point.
-    fn open<'a>(
-        ck: &Self::CommitterKey,
-        labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        point: &'a P::Point,
-        opening_challenge: F,
-        rands: impl IntoIterator<Item = &'a Self::Randomness>,
-        rng: Option<&mut dyn RngCore>,
-    ) -> Result<Self::Proof, Self::Error>
-    where
-        P: 'a,
-        Self::Randomness: 'a,
-        Self::Commitment: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::open_individual_opening_challenges(
-            ck,
-            labeled_polynomials,
-            commitments,
-            point,
-            &opening_challenges,
-            rands,
-            rng,
-        )
-    }
-
-    /// On input a list of labeled polynomials and a query set, `open` outputs a proof of evaluation
-    /// of the polynomials at the points in the query set.
-    fn batch_open<'a>(
-        ck: &Self::CommitterKey,
-        labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<P::Point>,
-        opening_challenge: F,
-        rands: impl IntoIterator<Item = &'a Self::Randomness>,
-        rng: Option<&mut dyn RngCore>,
-    ) -> Result<Self::BatchProof, Self::Error>
-    where
-        Self::Randomness: 'a,
-        Self::Commitment: 'a,
-        P: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::batch_open_individual_opening_challenges(
-            ck,
-            labeled_polynomials,
-            commitments,
-            query_set,
-            &opening_challenges,
-            rands,
-            rng,
-        )
-    }
-
-    /// Verifies that `values` are the evaluations at `point` of the polynomials
-    /// committed inside `commitments`.
-    fn check<'a>(
-        vk: &Self::VerifierKey,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        point: &'a P::Point,
-        values: impl IntoIterator<Item = F>,
-        proof: &Self::Proof,
-        opening_challenge: F,
-        rng: Option<&mut dyn RngCore>,
-    ) -> Result<bool, Self::Error>
-    where
-        Self::Commitment: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::check_individual_opening_challenges(
-            vk,
-            commitments,
-            &point,
-            values,
-            proof,
-            &opening_challenges,
-            rng,
-        )
-    }
-
-    /// Checks that `values` are the true evaluations at `query_set` of the polynomials
-    /// committed in `labeled_commitments`.
-    fn batch_check<'a, R: RngCore>(
-        vk: &Self::VerifierKey,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<P::Point>,
-        evaluations: &Evaluations<P::Point, F>,
-        proof: &Self::BatchProof,
-        opening_challenge: F,
-        rng: &mut R,
-    ) -> Result<bool, Self::Error>
-    where
-        Self::Commitment: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::batch_check_individual_opening_challenges(
-            vk,
-            commitments,
-            query_set,
-            evaluations,
-            proof,
-            &opening_challenges,
-            rng,
-        )
-    }
-
-    /// On input a list of polynomials, linear combinations of those polynomials,
-    /// and a query set, `open_combination` outputs a proof of evaluation of
-    /// the combinations at the points in the query set.
-    fn open_combinations<'a>(
-        ck: &Self::CommitterKey,
-        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<F>>,
-        polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<P::Point>,
-        opening_challenge: F,
-        rands: impl IntoIterator<Item = &'a Self::Randomness>,
-        rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<F, P, Self>, Self::Error>
-    where
-        P: 'a,
-        Self::Randomness: 'a,
-        Self::Commitment: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::open_combinations_individual_opening_challenges(
-            ck,
-            linear_combinations,
-            polynomials,
-            commitments,
-            query_set,
-            &opening_challenges,
-            rands,
-            rng,
-        )
-    }
-
-    /// Checks that `evaluations` are the true evaluations at `query_set` of the
-    /// linear combinations of polynomials committed in `commitments`.
-    fn check_combinations<'a, R: RngCore>(
-        vk: &Self::VerifierKey,
-        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<F>>,
-        commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        eqn_query_set: &QuerySet<P::Point>,
-        eqn_evaluations: &Evaluations<P::Point, F>,
-        proof: &BatchLCProof<F, P, Self>,
-        opening_challenge: F,
-        rng: &mut R,
-    ) -> Result<bool, Self::Error>
-    where
-        Self::Commitment: 'a,
-    {
-        let opening_challenges = |pow| opening_challenge.pow(&[pow]);
-        Self::check_combinations_individual_opening_challenges(
-            vk,
-            linear_combinations,
-            commitments,
-            eqn_query_set,
-            eqn_evaluations,
-            proof,
-            &opening_challenges,
-            rng,
-        )
-    }
-
     /// open but with individual challenges
-    fn open_individual_opening_challenges<'a>(
+    fn open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
-        opening_challenges: &dyn Fn(u64) -> F,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::Proof, Self::Error>
@@ -382,26 +222,26 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
         Self::Commitment: 'a;
 
     /// check but with individual challenges
-    fn check_individual_opening_challenges<'a>(
+    fn check<'a>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
         values: impl IntoIterator<Item = F>,
         proof: &Self::Proof,
-        opening_challenges: &dyn Fn(u64) -> F,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rng: Option<&mut dyn RngCore>,
     ) -> Result<bool, Self::Error>
     where
         Self::Commitment: 'a;
 
     /// batch_check but with individual challenges
-    fn batch_check_individual_opening_challenges<'a, R: RngCore>(
+    fn batch_check<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         evaluations: &Evaluations<P::Point, F>,
         proof: &Self::BatchProof,
-        opening_challenges: &dyn Fn(u64) -> F,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -442,13 +282,13 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
             }
 
             let proof_time = start_timer!(|| "Checking per-query proof");
-            result &= Self::check_individual_opening_challenges(
+            result &= Self::check(
                 vk,
                 comms,
                 &point,
                 values,
                 &proof,
-                opening_challenges,
+                challenge_generator,
                 Some(rng),
             )?;
             end_timer!(proof_time);
@@ -457,16 +297,16 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
     }
 
     /// open_combinations but with individual challenges
-    fn open_combinations_individual_opening_challenges<'a>(
+    fn open_combinations<'a>(
         ck: &Self::CommitterKey,
         linear_combinations: impl IntoIterator<Item = &'a LinearCombination<F>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> F,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<F, P, Self>, Self::Error>
+    ) -> Result<BatchLCProof<F, Self::BatchProof>, Self::Error>
     where
         Self::Randomness: 'a,
         Self::Commitment: 'a,
@@ -477,12 +317,12 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
         let poly_query_set =
             lc_query_set_to_poly_query_set(linear_combinations.iter().copied(), query_set);
         let poly_evals = evaluate_query_set(polynomials.iter().copied(), &poly_query_set);
-        let proof = Self::batch_open_individual_opening_challenges(
+        let proof = Self::batch_open(
             ck,
             polynomials,
             commitments,
             &poly_query_set,
-            opening_challenges,
+            challenge_generator,
             rands,
             rng,
         )?;
@@ -493,14 +333,14 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
     }
 
     /// check_combinations with individual challenges
-    fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
+    fn check_combinations<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         linear_combinations: impl IntoIterator<Item = &'a LinearCombination<F>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         eqn_query_set: &QuerySet<P::Point>,
         eqn_evaluations: &Evaluations<P::Point, F>,
-        proof: &BatchLCProof<F, P, Self>,
-        opening_challenges: &dyn Fn(u64) -> F,
+        proof: &BatchLCProof<F, Self::BatchProof>,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -546,13 +386,13 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
             }
         }
 
-        let pc_result = Self::batch_check_individual_opening_challenges(
+        let pc_result = Self::batch_check(
             vk,
             commitments,
             &poly_query_set,
             &poly_evals,
             proof,
-            opening_challenges,
+            challenge_generator,
             rng,
         )?;
         if !pc_result {
@@ -564,12 +404,12 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
     }
 
     /// batch_open with individual challenges
-    fn batch_open_individual_opening_challenges<'a>(
+    fn batch_open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> F,
+        challenge_generator: &mut ChallengeGenerator<F, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::BatchProof, Self::Error>
@@ -619,12 +459,12 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
             }
 
             let proof_time = start_timer!(|| "Creating proof");
-            let proof = Self::open_individual_opening_challenges(
+            let proof = Self::open(
                 ck,
                 query_polys,
                 query_comms,
                 &point,
-                opening_challenges,
+                challenge_generator,
                 query_rands,
                 Some(rng),
             )?;
@@ -639,6 +479,9 @@ pub trait PolynomialCommitment<F: Field, P: Polynomial<F>>: Sized {
     }
 }
 
+/// The size of opening challenges in bits.
+pub const CHALLENGE_SIZE: FieldElementSize = FieldElementSize::Truncated(128);
+
 /// Evaluate the given polynomials at `query_set`.
 pub fn evaluate_query_set<'a, F, P, T>(
     polys: impl IntoIterator<Item = &'a LabeledPolynomial<F, P>>,
@@ -683,8 +526,8 @@ fn lc_query_set_to_poly_query_set<'a, F: Field, T: Clone + Ord>(
 #[cfg(test)]
 pub mod tests {
     use crate::*;
-    use ark_ff::Field;
     use ark_poly::Polynomial;
+    use ark_sponge::poseidon::{PoseidonParameters, PoseidonSponge};
     use ark_std::rand::{
         distributions::{Distribution, Uniform},
         rngs::StdRng,
@@ -692,7 +535,7 @@ pub mod tests {
     };
     use ark_std::test_rng;
 
-    struct TestInfo<F: Field, P: Polynomial<F>> {
+    struct TestInfo<F: PrimeField, P: Polynomial<F>, S: CryptographicSponge> {
         num_iters: usize,
         max_degree: Option<usize>,
         supported_degree: Option<usize>,
@@ -703,102 +546,113 @@ pub mod tests {
         num_equations: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     }
 
-    pub fn bad_degree_bound_test<F, P, PC>(
+    pub fn bad_degree_bound_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
-        let rng = &mut test_rng();
-        let max_degree = 100;
-        let pp = PC::setup(max_degree, None, rng)?;
-        for _ in 0..10 {
-            let supported_degree = Uniform::from(1..=max_degree).sample(rng);
-            assert!(
-                max_degree >= supported_degree,
-                "max_degree < supported_degree"
-            );
-
-            let mut labels = Vec::new();
-            let mut polynomials = Vec::new();
-            let mut degree_bounds = Vec::new();
-
-            for i in 0..10 {
-                let label = format!("Test{}", i);
-                labels.push(label.clone());
-                let degree_bound = 1usize;
-                let hiding_bound = Some(1);
-                degree_bounds.push(degree_bound);
-
-                polynomials.push(LabeledPolynomial::new(
-                    label,
-                    rand_poly(supported_degree, None, rng),
-                    Some(degree_bound),
-                    hiding_bound,
-                ));
-            }
+        let challenge_generators = vec![
+            ChallengeGenerator::new_multivariate(sponge()),
+            ChallengeGenerator::new_univariate(&mut sponge()),
+        ];
+
+        for challenge_gen in challenge_generators {
+            let rng = &mut test_rng();
+            let max_degree = 100;
+            let pp = PC::setup(max_degree, None, rng)?;
+            for _ in 0..10 {
+                let supported_degree = Uniform::from(1..=max_degree).sample(rng);
+                assert!(
+                    max_degree >= supported_degree,
+                    "max_degree < supported_degree"
+                );
 
-            let supported_hiding_bound = polynomials
-                .iter()
-                .map(|p| p.hiding_bound().unwrap_or(0))
-                .max()
-                .unwrap_or(0);
-            println!("supported degree: {:?}", supported_degree);
-            println!("supported hiding bound: {:?}", supported_hiding_bound);
-            let (ck, vk) = PC::trim(
-                &pp,
-                supported_degree,
-                supported_hiding_bound,
-                Some(degree_bounds.as_slice()),
-            )?;
-            println!("Trimmed");
+                let mut labels = Vec::new();
+                let mut polynomials = Vec::new();
+                let mut degree_bounds = Vec::new();
+
+                for i in 0..10 {
+                    let label = format!("Test{}", i);
+                    labels.push(label.clone());
+                    let degree_bound = 1usize;
+                    let hiding_bound = Some(1);
+                    degree_bounds.push(degree_bound);
+
+                    polynomials.push(LabeledPolynomial::new(
+                        label,
+                        rand_poly(supported_degree, None, rng),
+                        Some(degree_bound),
+                        hiding_bound,
+                    ));
+                }
+
+                let supported_hiding_bound = polynomials
+                    .iter()
+                    .map(|p| p.hiding_bound().unwrap_or(0))
+                    .max()
+                    .unwrap_or(0);
+                println!("supported degree: {:?}", supported_degree);
+                println!("supported hiding bound: {:?}", supported_hiding_bound);
+                let (ck, vk) = PC::trim(
+                    &pp,
+                    supported_degree,
+                    supported_hiding_bound,
+                    Some(degree_bounds.as_slice()),
+                )?;
+                println!("Trimmed");
 
-            let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
+                let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
 
-            let mut query_set = QuerySet::new();
-            let mut values = Evaluations::new();
-            let point = rand_point(None, rng);
-            for (i, label) in labels.iter().enumerate() {
-                query_set.insert((label.clone(), (format!("{}", i), point.clone())));
-                let value = polynomials[i].evaluate(&point);
-                values.insert((label.clone(), point.clone()), value);
+                let mut query_set = QuerySet::new();
+                let mut values = Evaluations::new();
+                let point = rand_point(None, rng);
+                for (i, label) in labels.iter().enumerate() {
+                    query_set.insert((label.clone(), (format!("{}", i), point.clone())));
+                    let value = polynomials[i].evaluate(&point);
+                    values.insert((label.clone(), point.clone()), value);
+                }
+                println!("Generated query set");
+
+                let proof = PC::batch_open(
+                    &ck,
+                    &polynomials,
+                    &comms,
+                    &query_set,
+                    &mut (challenge_gen.clone()),
+                    &rands,
+                    Some(rng),
+                )?;
+                let result = PC::batch_check(
+                    &vk,
+                    &comms,
+                    &query_set,
+                    &values,
+                    &proof,
+                    &mut (challenge_gen.clone()),
+                    rng,
+                )?;
+                assert!(result, "proof was incorrect, Query set: {:#?}", query_set);
             }
-            println!("Generated query set");
-
-            let opening_challenge = F::rand(rng);
-            let proof = PC::batch_open(
-                &ck,
-                &polynomials,
-                &comms,
-                &query_set,
-                opening_challenge,
-                &rands,
-                Some(rng),
-            )?;
-            let result = PC::batch_check(
-                &vk,
-                &comms,
-                &query_set,
-                &values,
-                &proof,
-                opening_challenge,
-                rng,
-            )?;
-            assert!(result, "proof was incorrect, Query set: {:#?}", query_set);
         }
+
         Ok(())
     }
 
-    fn test_template<F, P, PC>(info: TestInfo<F, P>) -> Result<(), PC::Error>
+    fn test_template<F, P, PC, S>(info: TestInfo<F, P, S>) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let TestInfo {
             num_iters,
@@ -811,131 +665,139 @@ pub mod tests {
             num_equations: _,
             rand_poly,
             rand_point,
+            sponge,
         } = info;
 
-        let rng = &mut test_rng();
-        // If testing multivariate polynomials, make the max degree lower
-        let max_degree = match num_vars {
-            Some(_) => max_degree.unwrap_or(Uniform::from(2..=10).sample(rng)),
-            None => max_degree.unwrap_or(Uniform::from(2..=64).sample(rng)),
-        };
-        let pp = PC::setup(max_degree, num_vars, rng)?;
-
-        for _ in 0..num_iters {
-            let supported_degree =
-                supported_degree.unwrap_or(Uniform::from(1..=max_degree).sample(rng));
-            assert!(
-                max_degree >= supported_degree,
-                "max_degree < supported_degree"
-            );
-            let mut polynomials: Vec<LabeledPolynomial<F, P>> = Vec::new();
-            let mut degree_bounds = if enforce_degree_bounds {
-                Some(Vec::new())
-            } else {
-                None
+        let challenge_gens = vec![
+            ChallengeGenerator::new_multivariate(sponge()),
+            ChallengeGenerator::new_univariate(&mut sponge()),
+        ];
+
+        for challenge_gen in challenge_gens {
+            let rng = &mut test_rng();
+            // If testing multivariate polynomials, make the max degree lower
+            let max_degree = match num_vars {
+                Some(_) => max_degree.unwrap_or(Uniform::from(2..=10).sample(rng)),
+                None => max_degree.unwrap_or(Uniform::from(2..=64).sample(rng)),
             };
-
-            let mut labels = Vec::new();
-            println!("Sampled supported degree");
-
-            // Generate polynomials
-            let num_points_in_query_set = Uniform::from(1..=max_num_queries).sample(rng);
-            for i in 0..num_polynomials {
-                let label = format!("Test{}", i);
-                labels.push(label.clone());
-                let degree = Uniform::from(1..=supported_degree).sample(rng);
-                let degree_bound = if let Some(degree_bounds) = &mut degree_bounds {
-                    let range = Uniform::from(degree..=supported_degree);
-                    let degree_bound = range.sample(rng);
-                    degree_bounds.push(degree_bound);
-                    Some(degree_bound)
+            let pp = PC::setup(max_degree, num_vars, rng)?;
+
+            for _ in 0..num_iters {
+                let supported_degree =
+                    supported_degree.unwrap_or(Uniform::from(1..=max_degree).sample(rng));
+                assert!(
+                    max_degree >= supported_degree,
+                    "max_degree < supported_degree"
+                );
+                let mut polynomials: Vec<LabeledPolynomial<F, P>> = Vec::new();
+                let mut degree_bounds = if enforce_degree_bounds {
+                    Some(Vec::new())
                 } else {
                     None
                 };
 
-                let hiding_bound = if num_points_in_query_set >= degree {
-                    Some(degree)
-                } else {
-                    Some(num_points_in_query_set)
-                };
+                let mut labels = Vec::new();
+                println!("Sampled supported degree");
 
-                polynomials.push(LabeledPolynomial::new(
-                    label,
-                    rand_poly(degree, num_vars, rng).into(),
-                    degree_bound,
-                    hiding_bound,
-                ))
-            }
-            let supported_hiding_bound = polynomials
-                .iter()
-                .map(|p| p.hiding_bound().unwrap_or(0))
-                .max()
-                .unwrap_or(0);
-            println!("supported degree: {:?}", supported_degree);
-            println!("supported hiding bound: {:?}", supported_hiding_bound);
-            println!("num_points_in_query_set: {:?}", num_points_in_query_set);
-            let (ck, vk) = PC::trim(
-                &pp,
-                supported_degree,
-                supported_hiding_bound,
-                degree_bounds.as_ref().map(|s| s.as_slice()),
-            )?;
-            println!("Trimmed");
+                // Generate polynomials
+                let num_points_in_query_set = Uniform::from(1..=max_num_queries).sample(rng);
+                for i in 0..num_polynomials {
+                    let label = format!("Test{}", i);
+                    labels.push(label.clone());
+                    let degree = Uniform::from(1..=supported_degree).sample(rng);
+                    let degree_bound = if let Some(degree_bounds) = &mut degree_bounds {
+                        let range = Uniform::from(degree..=supported_degree);
+                        let degree_bound = range.sample(rng);
+                        degree_bounds.push(degree_bound);
+                        Some(degree_bound)
+                    } else {
+                        None
+                    };
 
-            let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
+                    let hiding_bound = if num_points_in_query_set >= degree {
+                        Some(degree)
+                    } else {
+                        Some(num_points_in_query_set)
+                    };
 
-            // Construct query set
-            let mut query_set = QuerySet::new();
-            let mut values = Evaluations::new();
-            for _ in 0..num_points_in_query_set {
-                let point = rand_point(num_vars, rng);
-                for (i, label) in labels.iter().enumerate() {
-                    query_set.insert((label.clone(), (format!("{}", i), point.clone())));
-                    let value = polynomials[i].evaluate(&point);
-                    values.insert((label.clone(), point.clone()), value);
+                    polynomials.push(LabeledPolynomial::new(
+                        label,
+                        rand_poly(degree, num_vars, rng).into(),
+                        degree_bound,
+                        hiding_bound,
+                    ))
                 }
-            }
-            println!("Generated query set");
-
-            let opening_challenge = F::rand(rng);
-            let proof = PC::batch_open(
-                &ck,
-                &polynomials,
-                &comms,
-                &query_set,
-                opening_challenge,
-                &rands,
-                Some(rng),
-            )?;
-            let result = PC::batch_check(
-                &vk,
-                &comms,
-                &query_set,
-                &values,
-                &proof,
-                opening_challenge,
-                rng,
-            )?;
-            if !result {
-                println!(
-                    "Failed with {} polynomials, num_points_in_query_set: {:?}",
-                    num_polynomials, num_points_in_query_set
-                );
-                println!("Degree of polynomials:",);
-                for poly in polynomials {
-                    println!("Degree: {:?}", poly.degree());
+                let supported_hiding_bound = polynomials
+                    .iter()
+                    .map(|p| p.hiding_bound().unwrap_or(0))
+                    .max()
+                    .unwrap_or(0);
+                println!("supported degree: {:?}", supported_degree);
+                println!("supported hiding bound: {:?}", supported_hiding_bound);
+                println!("num_points_in_query_set: {:?}", num_points_in_query_set);
+                let (ck, vk) = PC::trim(
+                    &pp,
+                    supported_degree,
+                    supported_hiding_bound,
+                    degree_bounds.as_ref().map(|s| s.as_slice()),
+                )?;
+                println!("Trimmed");
+
+                let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
+
+                // Construct query set
+                let mut query_set = QuerySet::new();
+                let mut values = Evaluations::new();
+                for _ in 0..num_points_in_query_set {
+                    let point = rand_point(num_vars, rng);
+                    for (i, label) in labels.iter().enumerate() {
+                        query_set.insert((label.clone(), (format!("{}", i), point.clone())));
+                        let value = polynomials[i].evaluate(&point);
+                        values.insert((label.clone(), point.clone()), value);
+                    }
                 }
+                println!("Generated query set");
+
+                let proof = PC::batch_open(
+                    &ck,
+                    &polynomials,
+                    &comms,
+                    &query_set,
+                    &mut (challenge_gen.clone()),
+                    &rands,
+                    Some(rng),
+                )?;
+                let result = PC::batch_check(
+                    &vk,
+                    &comms,
+                    &query_set,
+                    &values,
+                    &proof,
+                    &mut (challenge_gen.clone()),
+                    rng,
+                )?;
+                if !result {
+                    println!(
+                        "Failed with {} polynomials, num_points_in_query_set: {:?}",
+                        num_polynomials, num_points_in_query_set
+                    );
+                    println!("Degree of polynomials:",);
+                    for poly in polynomials {
+                        println!("Degree: {:?}", poly.degree());
+                    }
+                }
+                assert!(result, "proof was incorrect, Query set: {:#?}", query_set);
             }
-            assert!(result, "proof was incorrect, Query set: {:#?}", query_set);
         }
         Ok(())
     }
 
-    fn equation_test_template<F, P, PC>(info: TestInfo<F, P>) -> Result<(), PC::Error>
+    fn equation_test_template<F, P, PC, S>(info: TestInfo<F, P, S>) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let TestInfo {
             num_iters,
@@ -948,175 +810,184 @@ pub mod tests {
             num_equations,
             rand_poly,
             rand_point,
+            sponge,
         } = info;
 
-        let rng = &mut test_rng();
-        // If testing multivariate polynomials, make the max degree lower
-        let max_degree = match num_vars {
-            Some(_) => max_degree.unwrap_or(Uniform::from(2..=10).sample(rng)),
-            None => max_degree.unwrap_or(Uniform::from(2..=64).sample(rng)),
-        };
-        let pp = PC::setup(max_degree, num_vars, rng)?;
-
-        for _ in 0..num_iters {
-            let supported_degree =
-                supported_degree.unwrap_or(Uniform::from(1..=max_degree).sample(rng));
-            assert!(
-                max_degree >= supported_degree,
-                "max_degree < supported_degree"
-            );
-            let mut polynomials = Vec::new();
-            let mut degree_bounds = if enforce_degree_bounds {
-                Some(Vec::new())
-            } else {
-                None
+        let challenge_gens = vec![
+            ChallengeGenerator::new_multivariate(sponge()),
+            ChallengeGenerator::new_univariate(&mut sponge()),
+        ];
+
+        for challenge_gen in challenge_gens {
+            let rng = &mut test_rng();
+            // If testing multivariate polynomials, make the max degree lower
+            let max_degree = match num_vars {
+                Some(_) => max_degree.unwrap_or(Uniform::from(2..=10).sample(rng)),
+                None => max_degree.unwrap_or(Uniform::from(2..=64).sample(rng)),
             };
-
-            let mut labels = Vec::new();
-            println!("Sampled supported degree");
-
-            // Generate polynomials
-            let num_points_in_query_set = Uniform::from(1..=max_num_queries).sample(rng);
-            for i in 0..num_polynomials {
-                let label = format!("Test{}", i);
-                labels.push(label.clone());
-                let degree = Uniform::from(1..=supported_degree).sample(rng);
-                let degree_bound = if let Some(degree_bounds) = &mut degree_bounds {
-                    if rng.gen() {
-                        let range = Uniform::from(degree..=supported_degree);
-                        let degree_bound = range.sample(rng);
-                        degree_bounds.push(degree_bound);
-                        Some(degree_bound)
-                    } else {
-                        None
-                    }
+            let pp = PC::setup(max_degree, num_vars, rng)?;
+
+            for _ in 0..num_iters {
+                let supported_degree =
+                    supported_degree.unwrap_or(Uniform::from(1..=max_degree).sample(rng));
+                assert!(
+                    max_degree >= supported_degree,
+                    "max_degree < supported_degree"
+                );
+                let mut polynomials = Vec::new();
+                let mut degree_bounds = if enforce_degree_bounds {
+                    Some(Vec::new())
                 } else {
                     None
                 };
 
-                let hiding_bound = if num_points_in_query_set >= degree {
-                    Some(degree)
-                } else {
-                    Some(num_points_in_query_set)
-                };
-                println!("Hiding bound: {:?}", hiding_bound);
-
-                polynomials.push(LabeledPolynomial::new(
-                    label,
-                    rand_poly(degree, num_vars, rng),
-                    degree_bound,
-                    hiding_bound,
-                ))
-            }
-            println!("supported degree: {:?}", supported_degree);
-            println!("num_points_in_query_set: {:?}", num_points_in_query_set);
-            println!("{:?}", degree_bounds);
-            println!("{}", num_polynomials);
-            println!("{}", enforce_degree_bounds);
-
-            let (ck, vk) = PC::trim(
-                &pp,
-                supported_degree,
-                supported_degree,
-                degree_bounds.as_ref().map(|s| s.as_slice()),
-            )?;
-            println!("Trimmed");
-
-            let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
-
-            // Let's construct our equations
-            let mut linear_combinations = Vec::new();
-            let mut query_set = QuerySet::new();
-            let mut values = Evaluations::new();
-            for i in 0..num_points_in_query_set {
-                let point = rand_point(num_vars, rng);
-                for j in 0..num_equations.unwrap() {
-                    let label = format!("query {} eqn {}", i, j);
-                    let mut lc = LinearCombination::empty(label.clone());
-
-                    let mut value = F::zero();
-                    let should_have_degree_bounds: bool = rng.gen();
-                    for (k, label) in labels.iter().enumerate() {
-                        if should_have_degree_bounds {
-                            value += &polynomials[k].evaluate(&point);
-                            lc.push((F::one(), label.to_string().into()));
-                            break;
+                let mut labels = Vec::new();
+                println!("Sampled supported degree");
+
+                // Generate polynomials
+                let num_points_in_query_set = Uniform::from(1..=max_num_queries).sample(rng);
+                for i in 0..num_polynomials {
+                    let label = format!("Test{}", i);
+                    labels.push(label.clone());
+                    let degree = Uniform::from(1..=supported_degree).sample(rng);
+                    let degree_bound = if let Some(degree_bounds) = &mut degree_bounds {
+                        if rng.gen() {
+                            let range = Uniform::from(degree..=supported_degree);
+                            let degree_bound = range.sample(rng);
+                            degree_bounds.push(degree_bound);
+                            Some(degree_bound)
                         } else {
-                            let poly = &polynomials[k];
-                            if poly.degree_bound().is_some() {
-                                continue;
+                            None
+                        }
+                    } else {
+                        None
+                    };
+
+                    let hiding_bound = if num_points_in_query_set >= degree {
+                        Some(degree)
+                    } else {
+                        Some(num_points_in_query_set)
+                    };
+                    println!("Hiding bound: {:?}", hiding_bound);
+
+                    polynomials.push(LabeledPolynomial::new(
+                        label,
+                        rand_poly(degree, num_vars, rng),
+                        degree_bound,
+                        hiding_bound,
+                    ))
+                }
+                println!("supported degree: {:?}", supported_degree);
+                println!("num_points_in_query_set: {:?}", num_points_in_query_set);
+                println!("{:?}", degree_bounds);
+                println!("{}", num_polynomials);
+                println!("{}", enforce_degree_bounds);
+
+                let (ck, vk) = PC::trim(
+                    &pp,
+                    supported_degree,
+                    supported_degree,
+                    degree_bounds.as_ref().map(|s| s.as_slice()),
+                )?;
+                println!("Trimmed");
+
+                let (comms, rands) = PC::commit(&ck, &polynomials, Some(rng))?;
+
+                // Let's construct our equations
+                let mut linear_combinations = Vec::new();
+                let mut query_set = QuerySet::new();
+                let mut values = Evaluations::new();
+                for i in 0..num_points_in_query_set {
+                    let point = rand_point(num_vars, rng);
+                    for j in 0..num_equations.unwrap() {
+                        let label = format!("query {} eqn {}", i, j);
+                        let mut lc = LinearCombination::empty(label.clone());
+
+                        let mut value = F::zero();
+                        let should_have_degree_bounds: bool = rng.gen();
+                        for (k, label) in labels.iter().enumerate() {
+                            if should_have_degree_bounds {
+                                value += &polynomials[k].evaluate(&point);
+                                lc.push((F::one(), label.to_string().into()));
+                                break;
                             } else {
-                                assert!(poly.degree_bound().is_none());
-                                let coeff = F::rand(rng);
-                                value += &(coeff * poly.evaluate(&point));
-                                lc.push((coeff, label.to_string().into()));
+                                let poly = &polynomials[k];
+                                if poly.degree_bound().is_some() {
+                                    continue;
+                                } else {
+                                    assert!(poly.degree_bound().is_none());
+                                    let coeff = F::rand(rng);
+                                    value += &(coeff * poly.evaluate(&point));
+                                    lc.push((coeff, label.to_string().into()));
+                                }
                             }
                         }
+                        values.insert((label.clone(), point.clone()), value);
+                        if !lc.is_empty() {
+                            linear_combinations.push(lc);
+                            // Insert query
+                            query_set.insert((label.clone(), (format!("{}", i), point.clone())));
+                        }
                     }
-                    values.insert((label.clone(), point.clone()), value);
-                    if !lc.is_empty() {
-                        linear_combinations.push(lc);
-                        // Insert query
-                        query_set.insert((label.clone(), (format!("{}", i), point.clone())));
+                }
+                if linear_combinations.is_empty() {
+                    continue;
+                }
+                println!("Generated query set");
+                println!("Linear combinations: {:?}", linear_combinations);
+
+                let proof = PC::open_combinations(
+                    &ck,
+                    &linear_combinations,
+                    &polynomials,
+                    &comms,
+                    &query_set,
+                    &mut (challenge_gen.clone()),
+                    &rands,
+                    Some(rng),
+                )?;
+                println!("Generated proof");
+                let result = PC::check_combinations(
+                    &vk,
+                    &linear_combinations,
+                    &comms,
+                    &query_set,
+                    &values,
+                    &proof,
+                    &mut (challenge_gen.clone()),
+                    rng,
+                )?;
+                if !result {
+                    println!(
+                        "Failed with {} polynomials, num_points_in_query_set: {:?}",
+                        num_polynomials, num_points_in_query_set
+                    );
+                    println!("Degree of polynomials:",);
+                    for poly in polynomials {
+                        println!("Degree: {:?}", poly.degree());
                     }
                 }
-            }
-            if linear_combinations.is_empty() {
-                continue;
-            }
-            println!("Generated query set");
-            println!("Linear combinations: {:?}", linear_combinations);
-
-            let opening_challenge = F::rand(rng);
-            let proof = PC::open_combinations(
-                &ck,
-                &linear_combinations,
-                &polynomials,
-                &comms,
-                &query_set,
-                opening_challenge,
-                &rands,
-                Some(rng),
-            )?;
-            println!("Generated proof");
-            let result = PC::check_combinations(
-                &vk,
-                &linear_combinations,
-                &comms,
-                &query_set,
-                &values,
-                &proof,
-                opening_challenge,
-                rng,
-            )?;
-            if !result {
-                println!(
-                    "Failed with {} polynomials, num_points_in_query_set: {:?}",
-                    num_polynomials, num_points_in_query_set
+                assert!(
+                    result,
+                    "proof was incorrect, equations: {:#?}",
+                    linear_combinations
                 );
-                println!("Degree of polynomials:",);
-                for poly in polynomials {
-                    println!("Degree: {:?}", poly.degree());
-                }
             }
-            assert!(
-                result,
-                "proof was incorrect, equations: {:#?}",
-                linear_combinations
-            );
         }
         Ok(())
     }
 
-    pub fn single_poly_test<F, P, PC>(
+    pub fn single_poly_test<F, P, PC, S>(
         num_vars: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1129,18 +1000,21 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn linear_poly_degree_bound_test<F, P, PC>(
+    pub fn linear_poly_degree_bound_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1153,18 +1027,21 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn single_poly_degree_bound_test<F, P, PC>(
+    pub fn single_poly_degree_bound_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1177,18 +1054,21 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn quadratic_poly_degree_bound_multiple_queries_test<F, P, PC>(
+    pub fn quadratic_poly_degree_bound_multiple_queries_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1201,18 +1081,21 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn single_poly_degree_bound_multiple_queries_test<F, P, PC>(
+    pub fn single_poly_degree_bound_multiple_queries_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1225,18 +1108,21 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn two_polys_degree_bound_single_query_test<F, P, PC>(
+    pub fn two_polys_degree_bound_single_query_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1249,19 +1135,22 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn full_end_to_end_test<F, P, PC>(
+    pub fn full_end_to_end_test<F, P, PC, S>(
         num_vars: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1274,19 +1163,22 @@ pub mod tests {
             num_equations: None,
             rand_poly,
             rand_point,
+            sponge,
         };
-        test_template::<F, P, PC>(info)
+        test_template::<F, P, PC, S>(info)
     }
 
-    pub fn full_end_to_end_equation_test<F, P, PC>(
+    pub fn full_end_to_end_equation_test<F, P, PC, S>(
         num_vars: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1299,19 +1191,22 @@ pub mod tests {
             num_equations: Some(10),
             rand_poly,
             rand_point,
+            sponge,
         };
-        equation_test_template::<F, P, PC>(info)
+        equation_test_template::<F, P, PC, S>(info)
     }
 
-    pub fn single_equation_test<F, P, PC>(
+    pub fn single_equation_test<F, P, PC, S>(
         num_vars: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1324,19 +1219,22 @@ pub mod tests {
             num_equations: Some(1),
             rand_poly,
             rand_point,
+            sponge,
         };
-        equation_test_template::<F, P, PC>(info)
+        equation_test_template::<F, P, PC, S>(info)
     }
 
-    pub fn two_equation_test<F, P, PC>(
+    pub fn two_equation_test<F, P, PC, S>(
         num_vars: Option<usize>,
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1349,18 +1247,21 @@ pub mod tests {
             num_equations: Some(2),
             rand_poly,
             rand_point,
+            sponge,
         };
-        equation_test_template::<F, P, PC>(info)
+        equation_test_template::<F, P, PC, S>(info)
     }
 
-    pub fn two_equation_degree_bound_test<F, P, PC>(
+    pub fn two_equation_degree_bound_test<F, P, PC, S>(
         rand_poly: fn(usize, Option<usize>, &mut StdRng) -> P,
         rand_point: fn(Option<usize>, &mut StdRng) -> P::Point,
+        sponge: fn() -> S,
     ) -> Result<(), PC::Error>
     where
-        F: Field,
+        F: PrimeField,
         P: Polynomial<F>,
-        PC: PolynomialCommitment<F, P>,
+        PC: PolynomialCommitment<F, P, S>,
+        S: CryptographicSponge,
     {
         let info = TestInfo {
             num_iters: 100,
@@ -1373,7 +1274,41 @@ pub mod tests {
             num_equations: Some(2),
             rand_poly,
             rand_point,
+            sponge,
         };
-        equation_test_template::<F, P, PC>(info)
+        equation_test_template::<F, P, PC, S>(info)
+    }
+
+    pub(crate) fn poseidon_sponge_for_test<F: PrimeField>() -> PoseidonSponge<F> {
+        PoseidonSponge::new(&poseidon_parameters_for_test())
+    }
+
+    /// Generate default parameters for alpha = 17, state-size = 8
+    ///
+    /// WARNING: This poseidon parameter is not secure. Please generate
+    /// your own parameters according the field you use.
+    pub(crate) fn poseidon_parameters_for_test<F: PrimeField>() -> PoseidonParameters<F> {
+        let full_rounds = 8;
+        let partial_rounds = 31;
+        let alpha = 17;
+
+        let mds = vec![
+            vec![F::one(), F::zero(), F::one()],
+            vec![F::one(), F::one(), F::zero()],
+            vec![F::zero(), F::one(), F::one()],
+        ];
+
+        let mut ark = Vec::new();
+        let mut ark_rng = test_rng();
+
+        for _ in 0..(full_rounds + partial_rounds) {
+            let mut res = Vec::new();
+
+            for _ in 0..3 {
+                res.push(F::rand(&mut ark_rng));
+            }
+            ark.push(res);
+        }
+        PoseidonParameters::new(full_rounds, partial_rounds, alpha, mds, ark)
     }
 }
diff --git a/src/marlin/marlin_pc/mod.rs b/src/marlin/marlin_pc/mod.rs
index d101e3ae3ab10d511bf1f0a19dfc4370d47e1a83..918d5c4c30303386c2cb8840d237f20ab8881dda 100644
--- a/src/marlin/marlin_pc/mod.rs
+++ b/src/marlin/marlin_pc/mod.rs
@@ -1,4 +1,4 @@
-use crate::{kzg10, marlin::Marlin, PCCommitterKey};
+use crate::{kzg10, marlin::Marlin, PCCommitterKey, CHALLENGE_SIZE};
 use crate::{BTreeMap, BTreeSet, ToString, Vec};
 use crate::{BatchLCProof, Error, Evaluations, QuerySet};
 use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination};
@@ -10,6 +10,8 @@ use ark_std::rand::RngCore;
 use ark_std::{marker::PhantomData, ops::Div, vec};
 
 mod data_structures;
+use crate::challenge::ChallengeGenerator;
+use ark_sponge::CryptographicSponge;
 pub use data_structures::*;
 
 /// Polynomial commitment based on [[KZG10]][kzg], with degree enforcement, batching,
@@ -24,9 +26,10 @@ pub use data_structures::*;
 ///
 /// [kzg]: http://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf
 /// [marlin]: https://eprint.iacr.org/2019/104
-pub struct MarlinKZG10<E: PairingEngine, P: UVPolynomial<E::Fr>> {
+pub struct MarlinKZG10<E: PairingEngine, P: UVPolynomial<E::Fr>, S: CryptographicSponge> {
     _engine: PhantomData<E>,
     _poly: PhantomData<P>,
+    _sponge: PhantomData<S>,
 }
 
 pub(crate) fn shift_polynomial<E: PairingEngine, P: UVPolynomial<E::Fr>>(
@@ -50,10 +53,11 @@ pub(crate) fn shift_polynomial<E: PairingEngine, P: UVPolynomial<E::Fr>>(
     }
 }
 
-impl<E, P> PolynomialCommitment<E::Fr, P> for MarlinKZG10<E, P>
+impl<E, P, S> PolynomialCommitment<E::Fr, P, S> for MarlinKZG10<E, P, S>
 where
     E: PairingEngine,
     P: UVPolynomial<E::Fr, Point = E::Fr>,
+    S: CryptographicSponge,
     for<'a, 'b> &'a P: Div<&'b P, Output = P>,
 {
     type UniversalParams = UniversalParams<E>;
@@ -242,12 +246,12 @@ where
     }
 
     /// On input a polynomial `p` and a point `point`, outputs a proof for the same.
-    fn open_individual_opening_challenges<'a>(
+    fn open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::Proof, Self::Error>
@@ -263,7 +267,6 @@ where
         let mut shifted_r_witness = P::zero();
 
         let mut enforce_degree_bound = false;
-        let mut opening_challenge_counter = 0;
         for (polynomial, rand) in labeled_polynomials.into_iter().zip(rands) {
             let degree_bound = polynomial.degree_bound();
             assert_eq!(degree_bound.is_some(), rand.shifted_rand.is_some());
@@ -279,9 +282,8 @@ where
                 &polynomial,
             )?;
 
-            // compute challenge^j and challenge^{j+1}.
-            let challenge_j = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            // compute next challenges challenge^j and challenge^{j+1}.
+            let challenge_j = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
             assert_eq!(degree_bound.is_some(), rand.shifted_rand.is_some());
 
@@ -297,8 +299,7 @@ where
                         *point,
                         &shifted_rand,
                     )?;
-                let challenge_j_1 = opening_challenges(opening_challenge_counter);
-                opening_challenge_counter += 1;
+                let challenge_j_1 = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
                 let shifted_witness = shift_polynomial(ck, &witness, degree_bound);
 
@@ -340,13 +341,13 @@ where
 
     /// Verifies that `value` is the evaluation at `x` of the polynomial
     /// committed inside `comm`.
-    fn check_individual_opening_challenges<'a>(
+    fn check<'a>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
         values: impl IntoIterator<Item = E::Fr>,
         proof: &Self::Proof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<bool, Self::Error>
     where
@@ -354,7 +355,7 @@ where
     {
         let check_time = start_timer!(|| "Checking evaluations");
         let (combined_comm, combined_value) =
-            Marlin::accumulate_commitments_and_values_individual_opening_challenges(
+            Marlin::<E, S, P, Self>::accumulate_commitments_and_values(
                 commitments,
                 values,
                 opening_challenges,
@@ -366,25 +367,26 @@ where
         Ok(result)
     }
 
-    fn batch_check_individual_opening_challenges<'a, R: RngCore>(
+    fn batch_check<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         values: &Evaluations<E::Fr, P::Point>,
         proof: &Self::BatchProof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
         Self::Commitment: 'a,
     {
-        let (combined_comms, combined_queries, combined_evals) = Marlin::combine_and_normalize(
-            commitments,
-            query_set,
-            values,
-            opening_challenges,
-            Some(vk),
-        )?;
+        let (combined_comms, combined_queries, combined_evals) =
+            Marlin::<E, S, P, Self>::combine_and_normalize(
+                commitments,
+                query_set,
+                values,
+                opening_challenges,
+                Some(vk),
+            )?;
         assert_eq!(proof.len(), combined_queries.len());
         let proof_time = start_timer!(|| "Checking KZG10::Proof");
         let result = kzg10::KZG10::batch_check(
@@ -399,22 +401,22 @@ where
         Ok(result)
     }
 
-    fn open_combinations_individual_opening_challenges<'a>(
+    fn open_combinations<'a>(
         ck: &Self::CommitterKey,
         lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<E::Fr, P, Self>, Self::Error>
+    ) -> Result<BatchLCProof<E::Fr, Self::BatchProof>, Self::Error>
     where
         P: 'a,
         Self::Randomness: 'a,
         Self::Commitment: 'a,
     {
-        Marlin::open_combinations_individual_opening_challenges(
+        Marlin::<E, S, P, Self>::open_combinations(
             ck,
             lc_s,
             polynomials,
@@ -428,20 +430,20 @@ where
 
     /// Checks that `values` are the true evaluations at `query_set` of the polynomials
     /// committed in `labeled_commitments`.
-    fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
+    fn check_combinations<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         evaluations: &Evaluations<E::Fr, P::Point>,
-        proof: &BatchLCProof<E::Fr, P, Self>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        proof: &BatchLCProof<E::Fr, Self::BatchProof>,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
         Self::Commitment: 'a,
     {
-        Marlin::check_combinations_individual_opening_challenges(
+        Marlin::<E, S, P, Self>::check_combinations(
             vk,
             lc_s,
             commitments,
@@ -455,12 +457,12 @@ where
 
     /// On input a list of labeled polynomials and a query set, `open` outputs a proof of evaluation
     /// of the polynomials at the points in the query set.
-    fn batch_open_individual_opening_challenges<'a>(
+    fn batch_open<'a>(
         ck: &CommitterKey<E>,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Commitment<E>>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
     ) -> Result<Vec<kzg10::Proof<E>>, Error>
@@ -510,7 +512,7 @@ where
             }
 
             let proof_time = start_timer!(|| "Creating proof");
-            let proof = Self::open_individual_opening_challenges(
+            let proof = Self::open(
                 ck,
                 query_polys,
                 query_comms,
@@ -539,14 +541,19 @@ mod tests {
     use ark_ec::PairingEngine;
     use ark_ff::UniformRand;
     use ark_poly::{univariate::DensePolynomial as DensePoly, UVPolynomial};
+    use ark_sponge::poseidon::PoseidonSponge;
     use ark_std::rand::rngs::StdRng;
 
     type UniPoly_381 = DensePoly<<Bls12_381 as PairingEngine>::Fr>;
     type UniPoly_377 = DensePoly<<Bls12_377 as PairingEngine>::Fr>;
 
-    type PC<E, P> = MarlinKZG10<E, P>;
-    type PC_Bls12_381 = PC<Bls12_381, UniPoly_381>;
-    type PC_Bls12_377 = PC<Bls12_377, UniPoly_377>;
+    type PC<E, P, S> = MarlinKZG10<E, P, S>;
+
+    type Sponge_Bls12_381 = PoseidonSponge<<Bls12_381 as PairingEngine>::Fr>;
+    type Sponge_Bls12_377 = PoseidonSponge<<Bls12_377 as PairingEngine>::Fr>;
+
+    type PC_Bls12_381 = PC<Bls12_381, UniPoly_381, Sponge_Bls12_381>;
+    type PC_Bls12_377 = PC<Bls12_377, UniPoly_377, Sponge_Bls12_377>;
 
     fn rand_poly<E: PairingEngine>(
         degree: usize,
@@ -571,16 +578,18 @@ mod tests {
     #[test]
     fn single_poly_test() {
         use crate::tests::*;
-        single_poly_test::<_, _, PC_Bls12_377>(
+        single_poly_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_test::<_, _, PC_Bls12_381>(
+        single_poly_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -588,16 +597,18 @@ mod tests {
     #[test]
     fn constant_poly_test() {
         use crate::tests::*;
-        single_poly_test::<_, _, PC_Bls12_377>(
+        single_poly_test::<_, _, PC_Bls12_377, _>(
             None,
             constant_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_test::<_, _, PC_Bls12_381>(
+        single_poly_test::<_, _, PC_Bls12_381, _>(
             None,
             constant_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -605,14 +616,16 @@ mod tests {
     #[test]
     fn quadratic_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>(
+        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381>(
+        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -620,14 +633,16 @@ mod tests {
     #[test]
     fn linear_poly_degree_bound_test() {
         use crate::tests::*;
-        linear_poly_degree_bound_test::<_, _, PC_Bls12_377>(
+        linear_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        linear_poly_degree_bound_test::<_, _, PC_Bls12_381>(
+        linear_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -635,14 +650,16 @@ mod tests {
     #[test]
     fn single_poly_degree_bound_test() {
         use crate::tests::*;
-        single_poly_degree_bound_test::<_, _, PC_Bls12_377>(
+        single_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_degree_bound_test::<_, _, PC_Bls12_381>(
+        single_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -650,14 +667,16 @@ mod tests {
     #[test]
     fn single_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>(
+        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381>(
+        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -665,14 +684,16 @@ mod tests {
     #[test]
     fn two_polys_degree_bound_single_query_test() {
         use crate::tests::*;
-        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377>(
+        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_381>(
+        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -680,17 +701,19 @@ mod tests {
     #[test]
     fn full_end_to_end_test() {
         use crate::tests::*;
-        full_end_to_end_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -699,17 +722,19 @@ mod tests {
     #[test]
     fn single_equation_test() {
         use crate::tests::*;
-        single_equation_test::<_, _, PC_Bls12_377>(
+        single_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        single_equation_test::<_, _, PC_Bls12_381>(
+        single_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -718,17 +743,19 @@ mod tests {
     #[test]
     fn two_equation_test() {
         use crate::tests::*;
-        two_equation_test::<_, _, PC_Bls12_377>(
+        two_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        two_equation_test::<_, _, PC_Bls12_381>(
+        two_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -737,15 +764,17 @@ mod tests {
     #[test]
     fn two_equation_degree_bound_test() {
         use crate::tests::*;
-        two_equation_degree_bound_test::<_, _, PC_Bls12_377>(
+        two_equation_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        two_equation_degree_bound_test::<_, _, PC_Bls12_381>(
+        two_equation_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -754,17 +783,19 @@ mod tests {
     #[test]
     fn full_end_to_end_equation_test() {
         use crate::tests::*;
-        full_end_to_end_equation_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_equation_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -774,15 +805,17 @@ mod tests {
     #[should_panic]
     fn bad_degree_bound_test() {
         use crate::tests::*;
-        bad_degree_bound_test::<_, _, PC_Bls12_377>(
+        bad_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        bad_degree_bound_test::<_, _, PC_Bls12_381>(
+        bad_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
diff --git a/src/marlin/marlin_pst13_pc/mod.rs b/src/marlin/marlin_pst13_pc/mod.rs
index 9a268b23c36a26bb2c49c9045170b5835dfef0db..d418eb865c6180f7ff4c215febb9ece43711680c 100644
--- a/src/marlin/marlin_pst13_pc/mod.rs
+++ b/src/marlin/marlin_pst13_pc/mod.rs
@@ -1,6 +1,7 @@
 use crate::{
     kzg10,
     marlin::{marlin_pc, Marlin},
+    CHALLENGE_SIZE,
 };
 use crate::{BatchLCProof, Error, Evaluations, QuerySet};
 use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination};
@@ -21,6 +22,8 @@ pub use data_structures::*;
 mod combinations;
 use combinations::*;
 
+use crate::challenge::ChallengeGenerator;
+use ark_sponge::CryptographicSponge;
 #[cfg(feature = "parallel")]
 use rayon::prelude::*;
 
@@ -30,12 +33,13 @@ use rayon::prelude::*;
 ///
 /// [pst]: https://eprint.iacr.org/2011/587
 /// [marlin]: https://eprint.iacr.org/2019/104
-pub struct MarlinPST13<E: PairingEngine, P: MVPolynomial<E::Fr>> {
+pub struct MarlinPST13<E: PairingEngine, P: MVPolynomial<E::Fr>, S: CryptographicSponge> {
     _engine: PhantomData<E>,
     _poly: PhantomData<P>,
+    _sponge: PhantomData<S>,
 }
 
-impl<E: PairingEngine, P: MVPolynomial<E::Fr>> MarlinPST13<E, P> {
+impl<E: PairingEngine, P: MVPolynomial<E::Fr>, S: CryptographicSponge> MarlinPST13<E, P, S> {
     /// Given some point `z`, compute the quotients `w_i(X)` s.t
     ///
     /// `p(X) - p(z) = (X_1-z_1)*w_1(X) + (X_2-z_2)*w_2(X) + ... + (X_l-z_l)*w_l(X)`
@@ -136,10 +140,11 @@ impl<E: PairingEngine, P: MVPolynomial<E::Fr>> MarlinPST13<E, P> {
     }
 }
 
-impl<E, P> PolynomialCommitment<E::Fr, P> for MarlinPST13<E, P>
+impl<E, P, S> PolynomialCommitment<E::Fr, P, S> for MarlinPST13<E, P, S>
 where
     E: PairingEngine,
     P: MVPolynomial<E::Fr> + Sync,
+    S: CryptographicSponge,
     P::Point: Index<usize, Output = E::Fr>,
 {
     type UniversalParams = UniversalParams<E, P>;
@@ -438,12 +443,12 @@ where
     }
 
     /// On input a polynomial `p` and a point `point`, outputs a proof for the same.
-    fn open_individual_opening_challenges<'a>(
+    fn open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &P::Point,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::Proof, Self::Error>
@@ -455,13 +460,11 @@ where
         // Compute random linear combinations of committed polynomials and randomness
         let mut p = P::zero();
         let mut r = Randomness::empty();
-        let mut opening_challenge_counter = 0;
         for (polynomial, rand) in labeled_polynomials.into_iter().zip(rands) {
             Self::check_degrees_and_bounds(ck.supported_degree, &polynomial)?;
 
             // compute challenge^j and challenge^{j+1}.
-            let challenge_j = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            let challenge_j = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
             p += (challenge_j, polynomial.polynomial());
             r += (challenge_j, rand);
@@ -537,13 +540,13 @@ where
 
     /// Verifies that `value` is the evaluation at `x` of the polynomial
     /// committed inside `comm`.
-    fn check_individual_opening_challenges<'a>(
+    fn check<'a>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
         values: impl IntoIterator<Item = E::Fr>,
         proof: &Self::Proof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<bool, Self::Error>
     where
@@ -552,7 +555,7 @@ where
         let check_time = start_timer!(|| "Checking evaluations");
         // Accumulate commitments and values
         let (combined_comm, combined_value) =
-            Marlin::accumulate_commitments_and_values_individual_opening_challenges(
+            Marlin::<E, S, P, Self>::accumulate_commitments_and_values(
                 commitments,
                 values,
                 opening_challenges,
@@ -580,25 +583,26 @@ where
         Ok(lhs == rhs)
     }
 
-    fn batch_check_individual_opening_challenges<'a, R: RngCore>(
+    fn batch_check<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         values: &Evaluations<P::Point, E::Fr>,
         proof: &Self::BatchProof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
         Self::Commitment: 'a,
     {
-        let (combined_comms, combined_queries, combined_evals) = Marlin::combine_and_normalize(
-            commitments,
-            query_set,
-            values,
-            opening_challenges,
-            None,
-        )?;
+        let (combined_comms, combined_queries, combined_evals) =
+            Marlin::<E, S, P, Self>::combine_and_normalize(
+                commitments,
+                query_set,
+                values,
+                opening_challenges,
+                None,
+            )?;
         let check_time =
             start_timer!(|| format!("Checking {} evaluation proofs", combined_comms.len()));
         let g = vk.g.into_projective();
@@ -655,24 +659,24 @@ where
         Ok(result)
     }
 
-    fn open_combinations_individual_opening_challenges<'a>(
+    fn open_combinations<'a>(
         ck: &Self::CommitterKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<E::Fr, P, Self>, Self::Error>
+    ) -> Result<BatchLCProof<E::Fr, Self::BatchProof>, Self::Error>
     where
         P: 'a,
         Self::Randomness: 'a,
         Self::Commitment: 'a,
     {
-        Marlin::open_combinations_individual_opening_challenges(
+        Marlin::<E, S, P, Self>::open_combinations(
             ck,
-            lc_s,
+            linear_combinations,
             polynomials,
             commitments,
             query_set,
@@ -684,25 +688,25 @@ where
 
     /// Checks that `values` are the true evaluations at `query_set` of the polynomials
     /// committed in `labeled_commitments`.
-    fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
+    fn check_combinations<'a, R: RngCore>(
         vk: &Self::VerifierKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<P::Point>,
-        evaluations: &Evaluations<P::Point, E::Fr>,
-        proof: &BatchLCProof<E::Fr, P, Self>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        eqn_query_set: &QuerySet<P::Point>,
+        eqn_evaluations: &Evaluations<P::Point, E::Fr>,
+        proof: &BatchLCProof<E::Fr, Self::BatchProof>,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
         Self::Commitment: 'a,
     {
-        Marlin::check_combinations_individual_opening_challenges(
+        Marlin::<E, S, P, Self>::check_combinations(
             vk,
-            lc_s,
+            linear_combinations,
             commitments,
-            query_set,
-            evaluations,
+            eqn_query_set,
+            eqn_evaluations,
             proof,
             opening_challenges,
             rng,
@@ -722,14 +726,19 @@ mod tests {
         multivariate::{SparsePolynomial as SparsePoly, SparseTerm},
         MVPolynomial,
     };
+    use ark_sponge::poseidon::PoseidonSponge;
     use ark_std::rand::rngs::StdRng;
 
     type MVPoly_381 = SparsePoly<<Bls12_381 as PairingEngine>::Fr, SparseTerm>;
     type MVPoly_377 = SparsePoly<<Bls12_377 as PairingEngine>::Fr, SparseTerm>;
 
-    type PC<E, P> = MarlinPST13<E, P>;
-    type PC_Bls12_381 = PC<Bls12_381, MVPoly_381>;
-    type PC_Bls12_377 = PC<Bls12_377, MVPoly_377>;
+    type PC<E, P, S> = MarlinPST13<E, P, S>;
+
+    type Sponge_bls12_381 = PoseidonSponge<<Bls12_381 as PairingEngine>::Fr>;
+    type Sponge_Bls12_377 = PoseidonSponge<<Bls12_377 as PairingEngine>::Fr>;
+
+    type PC_Bls12_381 = PC<Bls12_381, MVPoly_381, Sponge_bls12_381>;
+    type PC_Bls12_377 = PC<Bls12_377, MVPoly_377, Sponge_Bls12_377>;
 
     fn rand_poly<E: PairingEngine>(
         degree: usize,
@@ -752,16 +761,18 @@ mod tests {
     fn single_poly_test() {
         use crate::tests::*;
         let num_vars = Some(10);
-        single_poly_test::<_, _, PC_Bls12_377>(
+        single_poly_test::<_, _, PC_Bls12_377, _>(
             num_vars,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_test::<_, _, PC_Bls12_381>(
+        single_poly_test::<_, _, PC_Bls12_381, _>(
             num_vars,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -770,17 +781,19 @@ mod tests {
     fn full_end_to_end_test() {
         use crate::tests::*;
         let num_vars = Some(10);
-        full_end_to_end_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_test::<_, _, PC_Bls12_377, _>(
             num_vars,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_test::<_, _, PC_Bls12_381, _>(
             num_vars,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -790,17 +803,19 @@ mod tests {
     fn single_equation_test() {
         use crate::tests::*;
         let num_vars = Some(10);
-        single_equation_test::<_, _, PC_Bls12_377>(
+        single_equation_test::<_, _, PC_Bls12_377, _>(
             num_vars,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        single_equation_test::<_, _, PC_Bls12_381>(
+        single_equation_test::<_, _, PC_Bls12_381, _>(
             num_vars,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -810,17 +825,19 @@ mod tests {
     fn two_equation_test() {
         use crate::tests::*;
         let num_vars = Some(10);
-        two_equation_test::<_, _, PC_Bls12_377>(
+        two_equation_test::<_, _, PC_Bls12_377, _>(
             num_vars,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        two_equation_test::<_, _, PC_Bls12_381>(
+        two_equation_test::<_, _, PC_Bls12_381, _>(
             num_vars,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -830,17 +847,19 @@ mod tests {
     fn full_end_to_end_equation_test() {
         use crate::tests::*;
         let num_vars = Some(10);
-        full_end_to_end_equation_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_377, _>(
             num_vars,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_equation_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_381, _>(
             num_vars,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
diff --git a/src/marlin/mod.rs b/src/marlin/mod.rs
index 12c919767af47a90a37fe9652598195b699bd7d4..05ba05703cdc4128cb407e38b74725e39c2e123c 100644
--- a/src/marlin/mod.rs
+++ b/src/marlin/mod.rs
@@ -1,3 +1,4 @@
+use crate::{challenge::ChallengeGenerator, CHALLENGE_SIZE};
 use crate::{kzg10, Error};
 use crate::{BTreeMap, BTreeSet, Debug, RngCore, String, ToString, Vec};
 use crate::{BatchLCProof, LabeledPolynomial, LinearCombination};
@@ -5,6 +6,7 @@ use crate::{Evaluations, LabeledCommitment, QuerySet};
 use crate::{PCRandomness, Polynomial, PolynomialCommitment};
 use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve};
 use ark_ff::{One, Zero};
+use ark_sponge::CryptographicSponge;
 use ark_std::{convert::TryInto, hash::Hash, ops::AddAssign};
 
 /// Polynomial commitment scheme from [[KZG10]][kzg] that enforces
@@ -24,11 +26,26 @@ pub mod marlin_pc;
 pub mod marlin_pst13_pc;
 
 /// Common functionalities between `marlin_pc` and `marlin_pst13_pc`
-struct Marlin<E: PairingEngine> {
+struct Marlin<E, S, P, PC>
+where
+    E: PairingEngine,
+    S: CryptographicSponge,
+    P: Polynomial<E::Fr>,
+    PC: PolynomialCommitment<E::Fr, P, S>,
+{
     _engine: core::marker::PhantomData<E>,
+    _sponge: core::marker::PhantomData<S>,
+    _poly: core::marker::PhantomData<P>,
+    _pc: core::marker::PhantomData<PC>,
 }
 
-impl<E: PairingEngine> Marlin<E> {
+impl<E, S, P, PC> Marlin<E, S, P, PC>
+where
+    E: PairingEngine,
+    S: CryptographicSponge,
+    P: Polynomial<E::Fr>,
+    PC: PolynomialCommitment<E::Fr, P, S>,
+{
     /// MSM for `commitments` and `coeffs`
     fn combine_commitments<'a>(
         coeffs_and_comms: impl IntoIterator<Item = (E::Fr, &'a marlin_pc::Commitment<E>)>,
@@ -87,31 +104,28 @@ impl<E: PairingEngine> Marlin<E> {
             .collect()
     }
 
-    /// Accumulate `commitments` and `values` according to `opening_challenge`.
-    fn accumulate_commitments_and_values_individual_opening_challenges<'a>(
+    /// Accumulate `commitments` and `values` according to the challenges produces by `challenge_gen`.
+    fn accumulate_commitments_and_values<'a>(
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<marlin_pc::Commitment<E>>>,
         values: impl IntoIterator<Item = E::Fr>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        challenge_gen: &mut ChallengeGenerator<E::Fr, S>,
         vk: Option<&marlin_pc::VerifierKey<E>>,
     ) -> Result<(E::G1Projective, E::Fr), Error> {
         let acc_time = start_timer!(|| "Accumulating commitments and values");
         let mut combined_comm = E::G1Projective::zero();
         let mut combined_value = E::Fr::zero();
-        let mut opening_challenge_counter = 0;
         for (labeled_commitment, value) in commitments.into_iter().zip(values) {
             let degree_bound = labeled_commitment.degree_bound();
             let commitment = labeled_commitment.commitment();
             assert_eq!(degree_bound.is_some(), commitment.shifted_comm.is_some());
 
-            let challenge_i = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            let challenge_i = challenge_gen.try_next_challenge_of_size(CHALLENGE_SIZE);
 
             combined_comm += &commitment.comm.0.mul(challenge_i);
             combined_value += &(value * &challenge_i);
 
             if let Some(degree_bound) = degree_bound {
-                let challenge_i_1 = opening_challenges(opening_challenge_counter);
-                opening_challenge_counter += 1;
+                let challenge_i_1 = challenge_gen.try_next_challenge_of_size(CHALLENGE_SIZE);
 
                 let shifted_comm = commitment
                     .shifted_comm
@@ -141,7 +155,7 @@ impl<E: PairingEngine> Marlin<E> {
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<marlin_pc::Commitment<E>>>,
         query_set: &QuerySet<D>,
         evaluations: &Evaluations<D, E::Fr>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         vk: Option<&marlin_pc::VerifierKey<E>>,
     ) -> Result<(Vec<kzg10::Commitment<E>>, Vec<D>, Vec<E::Fr>), Error>
     where
@@ -185,7 +199,7 @@ impl<E: PairingEngine> Marlin<E> {
                 values_to_combine.push(*v_i);
             }
 
-            let (c, v) = Marlin::accumulate_commitments_and_values_individual_opening_challenges(
+            let (c, v) = Self::accumulate_commitments_and_values(
                 comms_to_combine,
                 values_to_combine,
                 opening_challenges,
@@ -210,22 +224,23 @@ impl<E: PairingEngine> Marlin<E> {
     /// On input a list of polynomials, linear combinations of those polynomials,
     /// and a query set, `open_combination` outputs a proof of evaluation of
     /// the combinations at the points in the query set.
-    fn open_combinations_individual_opening_challenges<'a, P, D, PC>(
+    fn open_combinations<'a, D>(
         ck: &PC::CommitterKey,
         lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<PC::Commitment>>,
         query_set: &QuerySet<D>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a PC::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<E::Fr, P, PC>, Error>
+    ) -> Result<BatchLCProof<E::Fr, PC::BatchProof>, Error>
     where
         P: 'a + Polynomial<E::Fr, Point = D>,
         D: Debug + Clone + Hash + Ord + Sync,
         PC: PolynomialCommitment<
             E::Fr,
             P,
+            S,
             Commitment = marlin_pc::Commitment<E>,
             PreparedCommitment = marlin_pc::PreparedCommitment<E>,
             Error = Error,
@@ -281,18 +296,18 @@ impl<E: PairingEngine> Marlin<E> {
                 LabeledPolynomial::new(lc_label.clone(), poly, degree_bound, hiding_bound);
             lc_polynomials.push(lc_poly);
             lc_randomness.push(randomness);
-            lc_commitments.push(Marlin::combine_commitments(coeffs_and_comms));
+            lc_commitments.push(Self::combine_commitments(coeffs_and_comms));
             lc_info.push((lc_label, degree_bound));
         }
 
-        let comms = Marlin::normalize_commitments(lc_commitments);
+        let comms = Self::normalize_commitments(lc_commitments);
         let lc_commitments = lc_info
             .into_iter()
             .zip(comms)
             .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
             .collect::<Vec<_>>();
 
-        let proof = PC::batch_open_individual_opening_challenges(
+        let proof = PC::batch_open(
             ck,
             lc_polynomials.iter(),
             lc_commitments.iter(),
@@ -305,14 +320,14 @@ impl<E: PairingEngine> Marlin<E> {
         Ok(BatchLCProof { proof, evals: None })
     }
 
-    fn check_combinations_individual_opening_challenges<'a, R, P, D, PC>(
+    fn check_combinations<'a, R, D>(
         vk: &PC::VerifierKey,
         lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<PC::Commitment>>,
         query_set: &QuerySet<P::Point>,
         evaluations: &Evaluations<P::Point, E::Fr>,
-        proof: &BatchLCProof<E::Fr, P, PC>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        proof: &BatchLCProof<E::Fr, PC::BatchProof>,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Error>
     where
@@ -322,6 +337,7 @@ impl<E: PairingEngine> Marlin<E> {
         PC: PolynomialCommitment<
             E::Fr,
             P,
+            S,
             Commitment = marlin_pc::Commitment<E>,
             PreparedCommitment = marlin_pc::PreparedCommitment<E>,
             Error = Error,
@@ -373,13 +389,13 @@ impl<E: PairingEngine> Marlin<E> {
             }
             let lc_time =
                 start_timer!(|| format!("Combining {} commitments for {}", num_polys, lc_label));
-            lc_commitments.push(Marlin::combine_commitments(coeffs_and_comms));
+            lc_commitments.push(Self::combine_commitments(coeffs_and_comms));
             end_timer!(lc_time);
             lc_info.push((lc_label, degree_bound));
         }
         end_timer!(lc_processing_time);
         let combined_comms_norm_time = start_timer!(|| "Normalizing commitments");
-        let comms = Marlin::normalize_commitments(lc_commitments);
+        let comms = Self::normalize_commitments(lc_commitments);
         let lc_commitments = lc_info
             .into_iter()
             .zip(comms)
@@ -387,7 +403,7 @@ impl<E: PairingEngine> Marlin<E> {
             .collect::<Vec<_>>();
         end_timer!(combined_comms_norm_time);
 
-        PC::batch_check_individual_opening_challenges(
+        PC::batch_check(
             vk,
             &lc_commitments,
             &query_set,
diff --git a/src/sonic_pc/mod.rs b/src/sonic_pc/mod.rs
index 0782dfc1cd5b8733866e1b099bf0cded278c5f25..e02d635f2acf0f78d62e3318efe0dddffa585b22 100644
--- a/src/sonic_pc/mod.rs
+++ b/src/sonic_pc/mod.rs
@@ -1,4 +1,4 @@
-use crate::{kzg10, PCCommitterKey};
+use crate::{kzg10, PCCommitterKey, CHALLENGE_SIZE};
 use crate::{BTreeMap, BTreeSet, String, ToString, Vec};
 use crate::{BatchLCProof, Error, Evaluations, QuerySet, UVPolynomial};
 use crate::{LabeledCommitment, LabeledPolynomial, LinearCombination};
@@ -10,6 +10,8 @@ use ark_std::rand::RngCore;
 use ark_std::{convert::TryInto, marker::PhantomData, ops::Div, vec};
 
 mod data_structures;
+use crate::challenge::ChallengeGenerator;
+use ark_sponge::CryptographicSponge;
 pub use data_structures::*;
 
 /// Polynomial commitment based on [[KZG10]][kzg], with degree enforcement and
@@ -22,13 +24,19 @@ pub use data_structures::*;
 /// [sonic]: https://eprint.iacr.org/2019/099
 /// [al]: https://eprint.iacr.org/2019/601
 /// [marlin]: https://eprint.iacr.org/2019/1047
-pub struct SonicKZG10<E: PairingEngine, P: UVPolynomial<E::Fr>> {
+pub struct SonicKZG10<E: PairingEngine, P: UVPolynomial<E::Fr>, S: CryptographicSponge> {
     _engine: PhantomData<E>,
     _poly: PhantomData<P>,
+    _sponge: PhantomData<S>,
 }
 
-impl<E: PairingEngine, P: UVPolynomial<E::Fr>> SonicKZG10<E, P> {
-    fn accumulate_elems_individual_opening_challenges<'a>(
+impl<E, P, S> SonicKZG10<E, P, S>
+where
+    E: PairingEngine,
+    P: UVPolynomial<E::Fr>,
+    S: CryptographicSponge,
+{
+    fn accumulate_elems<'a>(
         combined_comms: &mut BTreeMap<Option<usize>, E::G1Projective>,
         combined_witness: &mut E::G1Projective,
         combined_adjusted_witness: &mut E::G1Projective,
@@ -37,14 +45,12 @@ impl<E: PairingEngine, P: UVPolynomial<E::Fr>> SonicKZG10<E, P> {
         point: P::Point,
         values: impl IntoIterator<Item = E::Fr>,
         proof: &kzg10::Proof<E>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         randomizer: Option<E::Fr>,
     ) {
         let acc_time = start_timer!(|| "Accumulating elements");
 
-        let mut opening_challenge_counter = 0;
-        let mut curr_challenge = opening_challenges(opening_challenge_counter);
-        opening_challenge_counter += 1;
+        let mut curr_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
         // Keeps track of running combination of values
         let mut combined_values = E::Fr::zero();
@@ -67,8 +73,7 @@ impl<E: PairingEngine, P: UVPolynomial<E::Fr>> SonicKZG10<E, P> {
             *combined_comms
                 .entry(degree_bound)
                 .or_insert(E::G1Projective::zero()) += &comm_with_challenge;
-            curr_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            curr_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
         }
 
         // Push expected results into list of elems. Power will be the negative of the expected power
@@ -129,10 +134,11 @@ impl<E: PairingEngine, P: UVPolynomial<E::Fr>> SonicKZG10<E, P> {
     }
 }
 
-impl<E, P> PolynomialCommitment<E::Fr, P> for SonicKZG10<E, P>
+impl<E, P, S> PolynomialCommitment<E::Fr, P, S> for SonicKZG10<E, P, S>
 where
     E: PairingEngine,
     P: UVPolynomial<E::Fr, Point = E::Fr>,
+    S: CryptographicSponge,
     for<'a, 'b> &'a P: Div<&'b P, Output = P>,
 {
     type UniversalParams = UniversalParams<E>;
@@ -335,12 +341,12 @@ where
         Ok((labeled_comms, randomness))
     }
 
-    fn open_individual_opening_challenges<'a>(
+    fn open<'a>(
         ck: &Self::CommitterKey,
         labeled_polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         _commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<Self::Proof, Self::Error>
@@ -352,10 +358,7 @@ where
         let mut combined_polynomial = P::zero();
         let mut combined_rand = kzg10::Randomness::empty();
 
-        let mut opening_challenge_counter = 0;
-
-        let mut curr_challenge = opening_challenges(opening_challenge_counter);
-        opening_challenge_counter += 1;
+        let mut curr_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
 
         for (polynomial, rand) in labeled_polynomials.into_iter().zip(rands) {
             let enforced_degree_bounds: Option<&[usize]> = ck
@@ -372,8 +375,7 @@ where
 
             combined_polynomial += (curr_challenge, polynomial.polynomial());
             combined_rand += (curr_challenge, rand);
-            curr_challenge = opening_challenges(opening_challenge_counter);
-            opening_challenge_counter += 1;
+            curr_challenge = opening_challenges.try_next_challenge_of_size(CHALLENGE_SIZE);
         }
 
         let proof_time = start_timer!(|| "Creating proof for polynomials");
@@ -383,13 +385,13 @@ where
         Ok(proof)
     }
 
-    fn check_individual_opening_challenges<'a>(
+    fn check<'a>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         point: &'a P::Point,
         values: impl IntoIterator<Item = E::Fr>,
         proof: &Self::Proof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         _rng: Option<&mut dyn RngCore>,
     ) -> Result<bool, Self::Error>
     where
@@ -400,7 +402,7 @@ where
         let mut combined_witness: E::G1Projective = E::G1Projective::zero();
         let mut combined_adjusted_witness: E::G1Projective = E::G1Projective::zero();
 
-        Self::accumulate_elems_individual_opening_challenges(
+        Self::accumulate_elems(
             &mut combined_comms,
             &mut combined_witness,
             &mut combined_adjusted_witness,
@@ -423,13 +425,13 @@ where
         res
     }
 
-    fn batch_check_individual_opening_challenges<'a, R: RngCore>(
+    fn batch_check<'a, R: RngCore>(
         vk: &Self::VerifierKey,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
         values: &Evaluations<E::Fr, P::Point>,
         proof: &Self::BatchProof,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -471,7 +473,7 @@ where
                 values_to_combine.push(*v_i);
             }
 
-            Self::accumulate_elems_individual_opening_challenges(
+            Self::accumulate_elems(
                 &mut combined_comms,
                 &mut combined_witness,
                 &mut combined_adjusted_witness,
@@ -495,16 +497,16 @@ where
         )
     }
 
-    fn open_combinations_individual_opening_challenges<'a>(
+    fn open_combinations<'a>(
         ck: &Self::CommitterKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         polynomials: impl IntoIterator<Item = &'a LabeledPolynomial<E::Fr, P>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
         query_set: &QuerySet<P::Point>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rands: impl IntoIterator<Item = &'a Self::Randomness>,
         rng: Option<&mut dyn RngCore>,
-    ) -> Result<BatchLCProof<E::Fr, P, Self>, Self::Error>
+    ) -> Result<BatchLCProof<E::Fr, Self::BatchProof>, Self::Error>
     where
         Self::Randomness: 'a,
         Self::Commitment: 'a,
@@ -522,7 +524,7 @@ where
         let mut lc_commitments = Vec::new();
         let mut lc_info = Vec::new();
 
-        for lc in lc_s {
+        for lc in linear_combinations {
             let lc_label = lc.label().clone();
             let mut poly = P::zero();
             let mut degree_bound = None;
@@ -576,7 +578,7 @@ where
             .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
             .collect::<Vec<_>>();
 
-        let proof = Self::batch_open_individual_opening_challenges(
+        let proof = Self::batch_open(
             ck,
             lc_polynomials.iter(),
             lc_commitments.iter(),
@@ -590,14 +592,14 @@ where
 
     /// Checks that `values` are the true evaluations at `query_set` of the polynomials
     /// committed in `labeled_commitments`.
-    fn check_combinations_individual_opening_challenges<'a, R: RngCore>(
+    fn check_combinations<'a, R: RngCore>(
         vk: &Self::VerifierKey,
-        lc_s: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
+        linear_combinations: impl IntoIterator<Item = &'a LinearCombination<E::Fr>>,
         commitments: impl IntoIterator<Item = &'a LabeledCommitment<Self::Commitment>>,
-        query_set: &QuerySet<P::Point>,
-        evaluations: &Evaluations<E::Fr, P::Point>,
-        proof: &BatchLCProof<E::Fr, P, Self>,
-        opening_challenges: &dyn Fn(u64) -> E::Fr,
+        eqn_query_set: &QuerySet<P::Point>,
+        eqn_evaluations: &Evaluations<P::Point, E::Fr>,
+        proof: &BatchLCProof<E::Fr, Self::BatchProof>,
+        opening_challenges: &mut ChallengeGenerator<E::Fr, S>,
         rng: &mut R,
     ) -> Result<bool, Self::Error>
     where
@@ -611,8 +613,8 @@ where
 
         let mut lc_commitments = Vec::new();
         let mut lc_info = Vec::new();
-        let mut evaluations = evaluations.clone();
-        for lc in lc_s {
+        let mut evaluations = eqn_evaluations.clone();
+        for lc in linear_combinations {
             let lc_label = lc.label().clone();
             let num_polys = lc.len();
 
@@ -661,10 +663,10 @@ where
             .map(|((label, d), c)| LabeledCommitment::new(label, c, d))
             .collect::<Vec<_>>();
 
-        Self::batch_check_individual_opening_challenges(
+        Self::batch_check(
             vk,
             &lc_commitments,
-            &query_set,
+            &eqn_query_set,
             &evaluations,
             proof,
             opening_challenges,
@@ -682,14 +684,17 @@ mod tests {
     use ark_ec::PairingEngine;
     use ark_ff::UniformRand;
     use ark_poly::{univariate::DensePolynomial as DensePoly, UVPolynomial};
+    use ark_sponge::poseidon::PoseidonSponge;
     use ark_std::rand::rngs::StdRng;
 
     type UniPoly_381 = DensePoly<<Bls12_381 as PairingEngine>::Fr>;
     type UniPoly_377 = DensePoly<<Bls12_377 as PairingEngine>::Fr>;
 
-    type PC<E, P> = SonicKZG10<E, P>;
-    type PC_Bls12_377 = PC<Bls12_377, UniPoly_377>;
-    type PC_Bls12_381 = PC<Bls12_381, UniPoly_381>;
+    type PC<E, P, S> = SonicKZG10<E, P, S>;
+    type Sponge_Bls12_377 = PoseidonSponge<<Bls12_377 as PairingEngine>::Fr>;
+    type Sponge_Bls12_381 = PoseidonSponge<<Bls12_381 as PairingEngine>::Fr>;
+    type PC_Bls12_377 = PC<Bls12_377, UniPoly_377, Sponge_Bls12_377>;
+    type PC_Bls12_381 = PC<Bls12_381, UniPoly_381, Sponge_Bls12_381>;
 
     fn rand_poly<E: PairingEngine>(
         degree: usize,
@@ -706,16 +711,18 @@ mod tests {
     #[test]
     fn single_poly_test() {
         use crate::tests::*;
-        single_poly_test::<_, _, PC_Bls12_377>(
+        single_poly_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_test::<_, _, PC_Bls12_381>(
+        single_poly_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -723,14 +730,16 @@ mod tests {
     #[test]
     fn quadratic_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>(
+        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381>(
+        quadratic_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -738,14 +747,16 @@ mod tests {
     #[test]
     fn linear_poly_degree_bound_test() {
         use crate::tests::*;
-        linear_poly_degree_bound_test::<_, _, PC_Bls12_377>(
+        linear_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        linear_poly_degree_bound_test::<_, _, PC_Bls12_381>(
+        linear_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -753,14 +764,16 @@ mod tests {
     #[test]
     fn single_poly_degree_bound_test() {
         use crate::tests::*;
-        single_poly_degree_bound_test::<_, _, PC_Bls12_377>(
+        single_poly_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_degree_bound_test::<_, _, PC_Bls12_381>(
+        single_poly_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -768,14 +781,16 @@ mod tests {
     #[test]
     fn single_poly_degree_bound_multiple_queries_test() {
         use crate::tests::*;
-        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377>(
+        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381>(
+        single_poly_degree_bound_multiple_queries_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -783,14 +798,16 @@ mod tests {
     #[test]
     fn two_polys_degree_bound_single_query_test() {
         use crate::tests::*;
-        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377>(
+        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
-        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_381>(
+        two_polys_degree_bound_single_query_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
     }
@@ -798,17 +815,19 @@ mod tests {
     #[test]
     fn full_end_to_end_test() {
         use crate::tests::*;
-        full_end_to_end_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -817,17 +836,19 @@ mod tests {
     #[test]
     fn single_equation_test() {
         use crate::tests::*;
-        single_equation_test::<_, _, PC_Bls12_377>(
+        single_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        single_equation_test::<_, _, PC_Bls12_381>(
+        single_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -836,17 +857,19 @@ mod tests {
     #[test]
     fn two_equation_test() {
         use crate::tests::*;
-        two_equation_test::<_, _, PC_Bls12_377>(
+        two_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        two_equation_test::<_, _, PC_Bls12_381>(
+        two_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -855,15 +878,17 @@ mod tests {
     #[test]
     fn two_equation_degree_bound_test() {
         use crate::tests::*;
-        two_equation_degree_bound_test::<_, _, PC_Bls12_377>(
+        two_equation_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        two_equation_degree_bound_test::<_, _, PC_Bls12_381>(
+        two_equation_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -872,17 +897,19 @@ mod tests {
     #[test]
     fn full_end_to_end_equation_test() {
         use crate::tests::*;
-        full_end_to_end_equation_test::<_, _, PC_Bls12_377>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_377, _>(
             None,
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        full_end_to_end_equation_test::<_, _, PC_Bls12_381>(
+        full_end_to_end_equation_test::<_, _, PC_Bls12_381, _>(
             None,
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");
@@ -892,15 +919,17 @@ mod tests {
     #[should_panic]
     fn bad_degree_bound_test() {
         use crate::tests::*;
-        bad_degree_bound_test::<_, _, PC_Bls12_377>(
+        bad_degree_bound_test::<_, _, PC_Bls12_377, _>(
             rand_poly::<Bls12_377>,
             rand_point::<Bls12_377>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-377");
         println!("Finished bls12-377");
-        bad_degree_bound_test::<_, _, PC_Bls12_381>(
+        bad_degree_bound_test::<_, _, PC_Bls12_381, _>(
             rand_poly::<Bls12_381>,
             rand_point::<Bls12_381>,
+            poseidon_sponge_for_test,
         )
         .expect("test failed for bls12-381");
         println!("Finished bls12-381");