diff --git a/halo2-base/src/gates/mod.rs b/halo2-base/src/gates/mod.rs index 3e96bdba..a353a4f4 100644 --- a/halo2-base/src/gates/mod.rs +++ b/halo2-base/src/gates/mod.rs @@ -6,7 +6,7 @@ pub mod flex_gate; pub mod range; /// Tests -#[cfg(any(test, feature = "test-utils"))] +#[cfg(test)] pub mod tests; pub use flex_gate::{GateChip, GateInstructions}; diff --git a/halo2-base/src/gates/tests/flex_gate_tests.rs b/halo2-base/src/gates/tests/flex_gate_tests.rs index b6d3e5ec..e73c6d63 100644 --- a/halo2-base/src/gates/tests/flex_gate_tests.rs +++ b/halo2-base/src/gates/tests/flex_gate_tests.rs @@ -1,3 +1,4 @@ +#![allow(clippy::type_complexity)] use super::*; use crate::halo2_proofs::dev::MockProver; use crate::halo2_proofs::dev::VerifyFailure; diff --git a/halo2-base/src/gates/tests/general.rs b/halo2-base/src/gates/tests/general.rs index 61b4f870..002130fe 100644 --- a/halo2-base/src/gates/tests/general.rs +++ b/halo2-base/src/gates/tests/general.rs @@ -1,13 +1,16 @@ -use super::*; use crate::gates::{ builder::{GateCircuitBuilder, GateThreadBuilder, RangeCircuitBuilder}, flex_gate::{GateChip, GateInstructions}, range::{RangeChip, RangeInstructions}, }; -use crate::halo2_proofs::dev::MockProver; +use crate::halo2_proofs::{ + dev::MockProver, + halo2curves::bn256::Fr, +}; use crate::utils::{BigPrimeField, ScalarField}; use crate::{Context, QuantumCell::Constant}; use ff::Field; +use rand::rngs::OsRng; use rayon::prelude::*; fn gate_tests(ctx: &mut Context, inputs: [F; 3]) { diff --git a/halo2-base/src/gates/tests/idx_to_indicator.rs b/halo2-base/src/gates/tests/idx_to_indicator.rs index 4db68e3e..0b0e6dce 100644 --- a/halo2-base/src/gates/tests/idx_to_indicator.rs +++ b/halo2-base/src/gates/tests/idx_to_indicator.rs @@ -7,15 +7,14 @@ use crate::{ plonk::keygen_pk, plonk::{keygen_vk, Assigned}, poly::kzg::commitment::ParamsKZG, + halo2curves::bn256::Fr, }, + utils::testing::{gen_proof, check_proof}, + QuantumCell::Witness, }; - use ff::Field; use itertools::Itertools; -use rand::{thread_rng, Rng}; - -use super::*; -use crate::QuantumCell::Witness; +use rand::{thread_rng, Rng, rngs::OsRng}; // soundness checks for `idx_to_indicator` function fn test_idx_to_indicator_gen(k: u32, len: usize) { diff --git a/halo2-base/src/gates/tests/mod.rs b/halo2-base/src/gates/tests/mod.rs index a12adeba..02b45335 100644 --- a/halo2-base/src/gates/tests/mod.rs +++ b/halo2-base/src/gates/tests/mod.rs @@ -1,73 +1,9 @@ -#![allow(clippy::type_complexity)] -use crate::halo2_proofs::{ - halo2curves::bn256::{Bn256, Fr, G1Affine}, - plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, - poly::commitment::ParamsProver, - poly::kzg::{ - commitment::KZGCommitmentScheme, commitment::ParamsKZG, multiopen::ProverSHPLONK, - multiopen::VerifierSHPLONK, strategy::SingleStrategy, - }, - transcript::{ - Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, - }, -}; -use rand::rngs::OsRng; +use crate::halo2_proofs::halo2curves::bn256::Fr; -#[cfg(test)] mod flex_gate_tests; -#[cfg(test)] mod general; -#[cfg(test)] mod idx_to_indicator; -#[cfg(test)] mod neg_prop_tests; -#[cfg(test)] mod pos_prop_tests; -#[cfg(test)] mod range_gate_tests; -#[cfg(test)] mod test_ground_truths; - -/// helper function to generate a proof with real prover -pub fn gen_proof( - params: &ParamsKZG, - pk: &ProvingKey, - circuit: impl Circuit, -) -> Vec { - let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); - create_proof::< - KZGCommitmentScheme, - ProverSHPLONK<'_, Bn256>, - Challenge255<_>, - _, - Blake2bWrite, G1Affine, _>, - _, - >(params, pk, &[circuit], &[&[]], OsRng, &mut transcript) - .expect("prover should not fail"); - transcript.finalize() -} - -/// helper function to verify a proof -pub fn check_proof( - params: &ParamsKZG, - vk: &VerifyingKey, - proof: &[u8], - expect_satisfied: bool, -) { - let verifier_params = params.verifier_params(); - let strategy = SingleStrategy::new(params); - let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof); - let res = verify_proof::< - KZGCommitmentScheme, - VerifierSHPLONK<'_, Bn256>, - Challenge255, - Blake2bRead<&[u8], G1Affine, Challenge255>, - SingleStrategy<'_, Bn256>, - >(verifier_params, vk, strategy, &[&[]], &mut transcript); - - if expect_satisfied { - assert!(res.is_ok()); - } else { - assert!(res.is_err()); - } -} diff --git a/halo2-base/src/gates/tests/test_ground_truths.rs b/halo2-base/src/gates/tests/test_ground_truths.rs index 894ff8c5..234cf636 100644 --- a/halo2-base/src/gates/tests/test_ground_truths.rs +++ b/halo2-base/src/gates/tests/test_ground_truths.rs @@ -1,3 +1,4 @@ +#![allow(clippy::type_complexity)] use num_integer::Integer; use crate::utils::biguint_to_fe; diff --git a/halo2-base/src/lib.rs b/halo2-base/src/lib.rs index 289d4057..5fd18ed7 100644 --- a/halo2-base/src/lib.rs +++ b/halo2-base/src/lib.rs @@ -1,4 +1,7 @@ //! Base library to build Halo2 circuits. +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +#![feature(const_cmp)] #![feature(stmt_expr_attributes)] #![feature(trait_alias)] #![deny(clippy::perf)] @@ -40,6 +43,8 @@ use utils::ScalarField; pub mod gates; /// Utility functions for converting between different types of field elements. pub mod utils; +/// Module for SafeType which enforce value range and realted functions. +pub mod safe_types; /// Constant representing whether the Layouter calls `synthesize` once just to get region shape. #[cfg(feature = "halo2-axiom")] diff --git a/halo2-base/src/safe_types/mod.rs b/halo2-base/src/safe_types/mod.rs new file mode 100644 index 00000000..63a8d526 --- /dev/null +++ b/halo2-base/src/safe_types/mod.rs @@ -0,0 +1,146 @@ +pub use crate::{ + gates::{ + flex_gate::GateInstructions, + range::{RangeChip, RangeInstructions}, + }, + utils::ScalarField, + AssignedValue, Context, + QuantumCell::{self, Constant, Existing, Witness}, +}; +use std::cmp::{max, min}; + +#[cfg(test)] +pub mod tests; + +type RawAssignedValues = Vec>; + +const BITS_PER_BYTE: usize = 8; + +/// SafeType's goal is to avoid out-of-range undefined behavior. +/// When building circuits, it's common to use mulitple AssignedValue to represent +/// a logical varaible. For example, we might want to represent a hash with 32 AssignedValue +/// where each AssignedValue represents 1 byte. However, the range of AssignedValue is much +/// larger than 1 byte(0~255). If a circuit takes 32 AssignedValue as inputs and some of them +/// are actually greater than 255, there could be some undefined behaviors. +/// SafeType gurantees the value range of its owned AssignedValue. So circuits don't need to +/// do any extra value checking if they take SafeType as inputs. +/// TOTAL_BITS is the number of total bits of this type. +/// BYTES_PER_ELE is the number of bytes of each element. +#[derive(Clone, Debug)] +pub struct SafeType { + // value is stored in little-endian. + value: RawAssignedValues, +} + +impl + SafeType +{ + /// Number of bytes of each element. + pub const BYTES_PER_ELE: usize = BYTES_PER_ELE; + /// Total bits of this type. + pub const TOTAL_BITS: usize = TOTAL_BITS; + /// Number of bits of each element. + pub const BITS_PER_ELE: usize = min(TOTAL_BITS, BYTES_PER_ELE * BITS_PER_BYTE); + /// Number of elements of this type. + pub const VALUE_LENGTH: usize = + (TOTAL_BITS + BYTES_PER_ELE * BITS_PER_BYTE - 1) / (BYTES_PER_ELE * BITS_PER_BYTE); + + // new is private so Safetype can only be constructed by this crate. + fn new(raw_values: RawAssignedValues) -> Self { + assert!(raw_values.len() == Self::VALUE_LENGTH, "Invalid raw values length"); + Self { value: raw_values } + } + + /// Return values in littile-endian. + pub fn value(&self) -> &RawAssignedValues { + &self.value + } +} + +/// Represent TOTAL_BITS with the least number of AssignedValue. +/// (2^(F::NUM_BITS) - 1) might not be a valid value for F. e.g. max value of F is a prime in [2^(F::NUM_BITS-1), 2^(F::NUM_BITS) - 1] +#[allow(type_alias_bounds)] +type CompactSafeType = + SafeType; + +/// SafeType for bool. +pub type SafeBool = CompactSafeType; +/// SafeType for uint8. +pub type SafeUint8 = CompactSafeType; +/// SafeType for uint16. +pub type SafeUint16 = CompactSafeType; +/// SafeType for uint32. +pub type SafeUint32 = CompactSafeType; +/// SafeType for uint64. +pub type SafeUint64 = CompactSafeType; +/// SafeType for uint128. +pub type SafeUint128 = CompactSafeType; +/// SafeType for uint256. +pub type SafeUint256 = CompactSafeType; +/// SafeType for bytes32. +pub type SafeBytes32 = SafeType; + +/// Chip for SafeType +pub struct SafeTypeChip<'a, F: ScalarField> { + range_chip: &'a RangeChip, +} + +impl<'a, F: ScalarField> SafeTypeChip<'a, F> { + /// Construct a SafeTypeChip. + pub fn new(range_chip: &'a RangeChip) -> Self { + Self { range_chip } + } + + /// Convert a vector of AssignedValue(treated as little-endian) to a SafeType. + /// The number of bytes of inputs must equal to the number of bytes of outputs. + /// This function also add contraints that a AssignedValue in inputs must be in the range of a byte. + pub fn raw_bytes_to( + &self, + ctx: &mut Context, + inputs: RawAssignedValues, + ) -> SafeType { + let element_bits = SafeType::::BITS_PER_ELE; + let bits = TOTAL_BITS; + assert!( + inputs.len() * BITS_PER_BYTE == max(bits, BITS_PER_BYTE), + "number of bits doesn't match" + ); + self.add_bytes_constraints(ctx, &inputs, bits); + // inputs is a bool or uint8. + if bits == 1 || element_bits == BITS_PER_BYTE { + return SafeType::::new(inputs); + }; + + let byte_base = (0..BYTES_PER_ELE) + .map(|i| Witness(self.range_chip.gate.pow_of_two[i * BITS_PER_BYTE])) + .collect::>(); + let value = inputs + .chunks(BYTES_PER_ELE) + .map(|chunk| { + self.range_chip.gate.inner_product( + ctx, + chunk.to_vec(), + byte_base[..chunk.len()].to_vec(), + ) + }) + .collect::>(); + SafeType::::new(value) + } + + fn add_bytes_constraints( + &self, + ctx: &mut Context, + inputs: &RawAssignedValues, + bits: usize, + ) { + let mut bits_left = bits; + for input in inputs { + let num_bit = min(bits_left, BITS_PER_BYTE); + self.range_chip.range_check(ctx, *input, num_bit); + bits_left -= num_bit; + } + } + + // TODO: Add comprasion. e.g. is_less_than(SafeUint8, SafeUint8) -> SafeBool + // TODO: Add type castings. e.g. uint256 -> bytes32/uint32 -> uint64 +} diff --git a/halo2-base/src/safe_types/tests.rs b/halo2-base/src/safe_types/tests.rs new file mode 100644 index 00000000..1f635053 --- /dev/null +++ b/halo2-base/src/safe_types/tests.rs @@ -0,0 +1,242 @@ +use crate::halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr, G1Affine}, + plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, + poly::commitment::ParamsProver, + poly::kzg::{ + commitment::KZGCommitmentScheme, commitment::ParamsKZG, multiopen::ProverSHPLONK, + multiopen::VerifierSHPLONK, strategy::SingleStrategy, + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + }, +}; + +use crate::{ + gates::{ + builder::{RangeCircuitBuilder, GateThreadBuilder}, + RangeChip, + }, + halo2_proofs::{ + plonk::keygen_pk, + plonk::{keygen_vk, Assigned}, + }, +}; +use rand::rngs::OsRng; +use itertools::Itertools; +use super::*; +use std::env; + +/// helper function to generate a proof with real prover +pub fn gen_proof( + params: &ParamsKZG, + pk: &ProvingKey, + circuit: impl Circuit, +) -> Vec { + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof::< + KZGCommitmentScheme, + ProverSHPLONK<'_, Bn256>, + Challenge255<_>, + _, + Blake2bWrite, G1Affine, _>, + _, + >(params, pk, &[circuit], &[&[]], OsRng, &mut transcript) + .expect("prover should not fail"); + transcript.finalize() +} + +/// helper function to verify a proof +pub fn check_proof( + params: &ParamsKZG, + vk: &VerifyingKey, + proof: &[u8], + expect_satisfied: bool, +) { + let verifier_params = params.verifier_params(); + let strategy = SingleStrategy::new(params); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof); + let res = verify_proof::< + KZGCommitmentScheme, + VerifierSHPLONK<'_, Bn256>, + Challenge255, + Blake2bRead<&[u8], G1Affine, Challenge255>, + SingleStrategy<'_, Bn256>, + >(verifier_params, vk, strategy, &[&[]], &mut transcript); + + if expect_satisfied { + assert!(res.is_ok()); + } else { + assert!(res.is_err()); + } +} + +// soundness checks for `raw_bytes_to` function +fn test_raw_bytes_to_gen(k: u32, raw_bytes: &[Fr], outputs: &[Fr], expect_satisfied: bool) { + // first create proving and verifying key + let mut builder = GateThreadBuilder::::keygen(); + let lookup_bits = 3; + env::set_var("LOOKUP_BITS", lookup_bits.to_string()); + let range_chip = RangeChip::::default(lookup_bits); + let safe_type_chip = SafeTypeChip::new(&range_chip); + + let dummy_raw_bytes = builder.main(0).assign_witnesses((0..raw_bytes.len()).map(|_| Fr::zero()).collect::>()); + + let safe_value = safe_type_chip.raw_bytes_to::( + builder.main(0), + dummy_raw_bytes); + // get the offsets of the safe value cells for later 'pranking' + let safe_value_offsets = safe_value.value().iter().map(|v| v.cell.unwrap().offset).collect::>(); + // set env vars + builder.config(k as usize, Some(9)); + let circuit = RangeCircuitBuilder::keygen(builder); + + let params = ParamsKZG::setup(k, OsRng); + // generate proving key + let vk = keygen_vk(¶ms, &circuit).unwrap(); + let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + let vk = pk.get_vk(); // pk consumed vk + + // now create different proofs to test the soundness of the circuit + let gen_pf = |inputs: &[Fr], outputs: &[Fr]| { + let mut builder = GateThreadBuilder::::prover(); + let range_chip = RangeChip::::default(lookup_bits); + let safe_type_chip = SafeTypeChip::new(&range_chip); + + let assigned_raw_bytes = builder.main(0).assign_witnesses(inputs.to_vec()); + safe_type_chip.raw_bytes_to::( + builder.main(0), + assigned_raw_bytes); + // prank the safe value cells + for (offset, witness) in safe_value_offsets.iter().zip_eq(outputs) { + builder.main(0).advice[*offset] = Assigned::::Trivial(*witness); + } + let circuit = RangeCircuitBuilder::prover(builder, vec![vec![]]); // no break points + gen_proof(¶ms, &pk, circuit) + }; + let pf = gen_pf(raw_bytes, outputs); + check_proof(¶ms, vk, &pf, expect_satisfied); +} + +#[test] +fn test_raw_bytes_to_bool() { + let k = 8; + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(0)], &[Fr::from(0)], true); + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(1)], &[Fr::from(1)], true); + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(1)], &[Fr::from(0)], false); + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(0)], &[Fr::from(1)], false); + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(3)], &[Fr::from(0)], false); + test_raw_bytes_to_gen::<1, 1>(k, &[Fr::from(3)], &[Fr::from(1)], false); +} + +#[test] +fn test_raw_bytes_to_uint256() { + const BYTES_PER_ELE: usize = SafeUint256::::BYTES_PER_ELE; + const TOTAL_BITS: usize = SafeUint256::::TOTAL_BITS; + let k = 11; + // [0x0; 32] -> [0x0, 0x0] + test_raw_bytes_to_gen::(k, &[Fr::from(0); 32], &[Fr::from(0), Fr::from(0)], true); + test_raw_bytes_to_gen::( + k, + &[[Fr::from(1)].as_slice(), [Fr::from(0); 31].as_slice()].concat(), + &[Fr::from(1), Fr::from(0)], true); + // [0x1, 0x2] + [0x0; 30] -> [0x201, 0x0] + test_raw_bytes_to_gen::( + k, + &[[Fr::from(1), Fr::from(2)].as_slice(), [Fr::from(0); 30].as_slice()].concat(), + &[Fr::from(0x201), Fr::from(0)], true); + // [[0xff; 32] -> [2^248 - 1, 0xff] + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 32], + &[Fr::from_raw([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff]), Fr::from(0xff)], true); + + // invalid raw_bytes, last bytes > 0xff + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0); 31].as_slice(), [Fr::from(0x1ff)].as_slice()].concat(), + &[Fr::from(0), Fr::from(0xff)], false); + // 0xff != 0xff00000000000000000000000000000000000000000000000000000000000000 + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0xff)].as_slice(), [Fr::from(0); 31].as_slice()].concat(), + &[Fr::from(0), Fr::from(0xff)], false); + // outputs overflow + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 32], + &[Fr::from_raw([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff]), Fr::from(0x1ff)], false); +} + +#[test] +fn test_raw_bytes_to_uint64() { + const BYTES_PER_ELE: usize = SafeUint64::::BYTES_PER_ELE; + const TOTAL_BITS: usize = SafeUint64::::TOTAL_BITS; + let k = 10; + // [0x0; 8] -> [0x0] + test_raw_bytes_to_gen::(k, &[Fr::from(0); 8], &[Fr::from(0)], true); + // [0x1, 0x2] + [0x0; 6] -> [0x201] + test_raw_bytes_to_gen::( + k, + &[[Fr::from(1), Fr::from(2)].as_slice(), [Fr::from(0); 6].as_slice()].concat(), + &[Fr::from(0x201)], true); + // [[0xff; 8] -> [2^64-1] + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 8], + &[Fr::from(0xffffffffffffffff)], true); + + // invalid raw_bytes, last bytes > 0xff + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0); 7].as_slice(), [Fr::from(0x1ff)].as_slice()].concat(), + &[Fr::from(0xff00000000000000)], false); + // 0xff != 0xff00000000000000000000000000000000000000000000000000000000000000 + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0xff)].as_slice(), [Fr::from(0); 7].as_slice()].concat(), + &[Fr::from(0xff00000000000000)], false); + // outputs overflow + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 8], + &[Fr::from_raw([0xffffffffffffffff, 0x1, 0x0, 0x0])], false); +} + +#[test] +fn test_raw_bytes_to_bytes32() { + const BYTES_PER_ELE: usize = SafeBytes32::::BYTES_PER_ELE; + const TOTAL_BITS: usize = SafeBytes32::::TOTAL_BITS; + let k = 10; + // [0x0; 32] -> [0x0; 32] + test_raw_bytes_to_gen::(k, &[Fr::from(0); 32], &[Fr::from(0); 32], true); + test_raw_bytes_to_gen::( + k, + &[[Fr::from(1)].as_slice(), [Fr::from(0); 31].as_slice()].concat(), + &[[Fr::from(1)].as_slice(), [Fr::from(0); 31].as_slice()].concat(), true); + // [0x1, 0x2] + [0x0; 30] -> [0x201, 0x0] + test_raw_bytes_to_gen::( + k, + &[[Fr::from(1), Fr::from(2)].as_slice(), [Fr::from(0); 30].as_slice()].concat(), + &[[Fr::from(1), Fr::from(2)].as_slice(), [Fr::from(0); 30].as_slice()].concat(), true); + // [[0xff; 32] -> [2^248 - 1, 0xff] + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 32], + &[Fr::from(0xff); 32], true); + + // invalid raw_bytes, last bytes > 0xff + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0); 31].as_slice(), [Fr::from(0x1ff)].as_slice()].concat(), + &[[Fr::from(0); 31].as_slice(), [Fr::from(0x1ff)].as_slice()].concat(), false); + // 0xff != 0xff00000000000000000000000000000000000000000000000000000000000000 + test_raw_bytes_to_gen::( + k, + &[[Fr::from(0xff)].as_slice(), [Fr::from(0); 31].as_slice()].concat(), + &[[Fr::from(0); 31].as_slice(), [Fr::from(0xff)].as_slice()].concat(), false); + // outputs overflow + test_raw_bytes_to_gen::( + k, + &[Fr::from(0xff); 32], + &[Fr::from(0x1ff); 32], false); +} \ No newline at end of file diff --git a/halo2-base/src/utils.rs b/halo2-base/src/utils.rs index 2856b267..2f11cc82 100644 --- a/halo2-base/src/utils.rs +++ b/halo2-base/src/utils.rs @@ -480,6 +480,68 @@ pub mod fs { } } +/// Utilities for testing +#[cfg(any(test, feature = "test-utils"))] +pub mod testing { + use crate::halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr, G1Affine}, + plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, + poly::commitment::ParamsProver, + poly::kzg::{ + commitment::KZGCommitmentScheme, commitment::ParamsKZG, multiopen::ProverSHPLONK, + multiopen::VerifierSHPLONK, strategy::SingleStrategy, + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + }, + }; + use rand::rngs::OsRng; + + /// helper function to generate a proof with real prover + pub fn gen_proof( + params: &ParamsKZG, + pk: &ProvingKey, + circuit: impl Circuit, + ) -> Vec { + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof::< + KZGCommitmentScheme, + ProverSHPLONK<'_, Bn256>, + Challenge255<_>, + _, + Blake2bWrite, G1Affine, _>, + _, + >(params, pk, &[circuit], &[&[]], OsRng, &mut transcript) + .expect("prover should not fail"); + transcript.finalize() + } + + /// helper function to verify a proof + pub fn check_proof( + params: &ParamsKZG, + vk: &VerifyingKey, + proof: &[u8], + expect_satisfied: bool, + ) { + let verifier_params = params.verifier_params(); + let strategy = SingleStrategy::new(params); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof); + let res = verify_proof::< + KZGCommitmentScheme, + VerifierSHPLONK<'_, Bn256>, + Challenge255, + Blake2bRead<&[u8], G1Affine, Challenge255>, + SingleStrategy<'_, Bn256>, + >(verifier_params, vk, strategy, &[&[]], &mut transcript); + + if expect_satisfied { + assert!(res.is_ok()); + } else { + assert!(res.is_err()); + } + } +} + #[cfg(test)] mod tests { use crate::halo2_proofs::halo2curves::bn256::Fr; diff --git a/halo2-ecc/src/fields/tests/fp/assert_eq.rs b/halo2-ecc/src/fields/tests/fp/assert_eq.rs index 5aac74bf..a8184594 100644 --- a/halo2-ecc/src/fields/tests/fp/assert_eq.rs +++ b/halo2-ecc/src/fields/tests/fp/assert_eq.rs @@ -4,13 +4,13 @@ use ff::Field; use halo2_base::{ gates::{ builder::{GateThreadBuilder, RangeCircuitBuilder}, - tests::{check_proof, gen_proof}, RangeChip, }, halo2_proofs::{ halo2curves::bn256::Fq, plonk::keygen_pk, plonk::keygen_vk, poly::kzg::commitment::ParamsKZG, }, + utils::testing::{check_proof, gen_proof}, }; use crate::{bn254::FpChip, fields::FieldChip};