From d870e47f4f2366878540195aa2d60816abfe6349 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Wed, 8 Mar 2023 20:37:00 -0800 Subject: [PATCH 1/3] chore: update halo2-ecc version to 0.3.0 --- halo2-ecc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2-ecc/Cargo.toml b/halo2-ecc/Cargo.toml index 0d5041b2..a2b4fe07 100644 --- a/halo2-ecc/Cargo.toml +++ b/halo2-ecc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "halo2-ecc" -version = "0.2.2" +version = "0.3.0" edition = "2021" [dependencies] From d828a87d372475b54b61439fee0b3ae429e8ad50 Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Fri, 17 Mar 2023 08:56:58 -0700 Subject: [PATCH 2/3] BUG: `FpChip::assert_equal` had `a` instead of `b` typo --- halo2-ecc/src/fields/fp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2-ecc/src/fields/fp.rs b/halo2-ecc/src/fields/fp.rs index a97f1d11..6099a147 100644 --- a/halo2-ecc/src/fields/fp.rs +++ b/halo2-ecc/src/fields/fp.rs @@ -368,7 +368,7 @@ impl<'range, F: PrimeField, Fp: PrimeField> FieldChip for FpChip<'range, F, F self.enforce_less_than_p(ctx, a); self.enforce_less_than_p(ctx, b); // a.native and b.native are derived from `a.truncation, b.truncation`, so no need to check if they're equal - for (limb_a, limb_b) in a.truncation.limbs.iter().zip(a.truncation.limbs.iter()) { + for (limb_a, limb_b) in a.truncation.limbs.iter().zip(b.truncation.limbs.iter()) { ctx.constrain_equal(limb_a, limb_b); } } From ae80a4885ba8efc2b70fad7c2d1669412266f82e Mon Sep 17 00:00:00 2001 From: Jonathan Wang Date: Mon, 17 Apr 2023 01:17:08 +0200 Subject: [PATCH 3/3] feat: add tests for `FpChip::assert_eq` * positive and negative tests --- halo2-base/Cargo.toml | 4 + halo2-base/src/gates/mod.rs | 2 +- halo2-base/src/gates/tests/general.rs | 170 +++++++++++++++++ .../src/gates/tests/idx_to_indicator.rs | 15 +- halo2-base/src/gates/tests/mod.rs | 176 +----------------- halo2-ecc/Cargo.toml | 1 + halo2-ecc/src/fields/tests.rs | 153 --------------- halo2-ecc/src/fields/tests/fp/assert_eq.rs | 85 +++++++++ halo2-ecc/src/fields/tests/fp/mod.rs | 75 ++++++++ halo2-ecc/src/fields/tests/fp12/mod.rs | 75 ++++++++ halo2-ecc/src/fields/tests/mod.rs | 2 + 11 files changed, 429 insertions(+), 329 deletions(-) create mode 100644 halo2-base/src/gates/tests/general.rs delete mode 100644 halo2-ecc/src/fields/tests.rs create mode 100644 halo2-ecc/src/fields/tests/fp/assert_eq.rs create mode 100644 halo2-ecc/src/fields/tests/fp/mod.rs create mode 100644 halo2-ecc/src/fields/tests/fp12/mod.rs create mode 100644 halo2-ecc/src/fields/tests/mod.rs diff --git a/halo2-base/Cargo.toml b/halo2-base/Cargo.toml index d151cf14..b5dcdd71 100644 --- a/halo2-base/Cargo.toml +++ b/halo2-base/Cargo.toml @@ -23,6 +23,9 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi plotters = { version = "0.3.0", optional = true } tabbycat = { version = "0.1", features = ["attributes"], optional = true } +# test-utils +rand = { version = "0.8", optional = true } + [dev-dependencies] ark-std = { version = "0.3.0", features = ["print-trace"] } rand = "0.8" @@ -44,6 +47,7 @@ halo2-pse = ["halo2_proofs"] halo2-axiom = ["halo2_proofs_axiom"] display = [] profile = ["halo2_proofs_axiom?/profile"] +test-utils = ["dep:rand"] [[bench]] name = "mul" diff --git a/halo2-base/src/gates/mod.rs b/halo2-base/src/gates/mod.rs index 705542b1..cd25c06f 100644 --- a/halo2-base/src/gates/mod.rs +++ b/halo2-base/src/gates/mod.rs @@ -2,7 +2,7 @@ pub mod builder; pub mod flex_gate; pub mod range; -#[cfg(test)] +#[cfg(any(test, feature = "test-utils"))] pub mod tests; pub use flex_gate::{GateChip, GateInstructions}; diff --git a/halo2-base/src/gates/tests/general.rs b/halo2-base/src/gates/tests/general.rs new file mode 100644 index 00000000..61b4f870 --- /dev/null +++ b/halo2-base/src/gates/tests/general.rs @@ -0,0 +1,170 @@ +use super::*; +use crate::gates::{ + builder::{GateCircuitBuilder, GateThreadBuilder, RangeCircuitBuilder}, + flex_gate::{GateChip, GateInstructions}, + range::{RangeChip, RangeInstructions}, +}; +use crate::halo2_proofs::dev::MockProver; +use crate::utils::{BigPrimeField, ScalarField}; +use crate::{Context, QuantumCell::Constant}; +use ff::Field; +use rayon::prelude::*; + +fn gate_tests(ctx: &mut Context, inputs: [F; 3]) { + let [a, b, c]: [_; 3] = ctx.assign_witnesses(inputs).try_into().unwrap(); + let chip = GateChip::default(); + + // test add + chip.add(ctx, a, b); + + // test sub + chip.sub(ctx, a, b); + + // test multiply + chip.mul(ctx, c, b); + + // test idx_to_indicator + chip.idx_to_indicator(ctx, Constant(F::from(3u64)), 4); + + let bits = ctx.assign_witnesses([F::zero(), F::one()]); + chip.bits_to_indicator(ctx, &bits); + + chip.is_equal(ctx, b, a); + + chip.is_zero(ctx, a); +} + +#[test] +fn test_gates() { + let k = 6; + let inputs = [10u64, 12u64, 120u64].map(Fr::from); + let mut builder = GateThreadBuilder::mock(); + gate_tests(builder.main(0), inputs); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = GateCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[test] +fn test_multithread_gates() { + let k = 6; + let inputs = [10u64, 12u64, 120u64].map(Fr::from); + let mut builder = GateThreadBuilder::mock(); + gate_tests(builder.main(0), inputs); + + let thread_ids = (0..4usize).map(|_| builder.get_new_thread_id()).collect::>(); + let new_threads = thread_ids + .into_par_iter() + .map(|id| { + let mut ctx = Context::new(builder.witness_gen_only(), id); + gate_tests(&mut ctx, [(); 3].map(|_| Fr::random(OsRng))); + ctx + }) + .collect::>(); + builder.threads[0].extend(new_threads); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = GateCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[cfg(feature = "dev-graph")] +#[test] +fn plot_gates() { + let k = 5; + use plotters::prelude::*; + + let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Gates Layout", ("sans-serif", 60)).unwrap(); + + let inputs = [Fr::zero(); 3]; + let builder = GateThreadBuilder::new(false); + gate_tests(builder.main(0), inputs); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = GateCircuitBuilder::keygen(builder); + halo2_proofs::dev::CircuitLayout::default().render(k, &circuit, &root).unwrap(); +} + +fn range_tests( + ctx: &mut Context, + lookup_bits: usize, + inputs: [F; 2], + range_bits: usize, + lt_bits: usize, +) { + let [a, b]: [_; 2] = ctx.assign_witnesses(inputs).try_into().unwrap(); + let chip = RangeChip::default(lookup_bits); + std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); + + chip.range_check(ctx, a, range_bits); + + chip.check_less_than(ctx, a, b, lt_bits); + + chip.is_less_than(ctx, a, b, lt_bits); + + chip.is_less_than(ctx, b, a, lt_bits); + + chip.div_mod(ctx, a, 7u64, lt_bits); +} + +#[test] +fn test_range_single() { + let k = 11; + let inputs = [100, 101].map(Fr::from); + let mut builder = GateThreadBuilder::mock(); + range_tests(builder.main(0), 3, inputs, 8, 8); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = RangeCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[test] +fn test_range_multicolumn() { + let k = 5; + let inputs = [100, 101].map(Fr::from); + let mut builder = GateThreadBuilder::mock(); + range_tests(builder.main(0), 3, inputs, 8, 8); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = RangeCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[cfg(feature = "dev-graph")] +#[test] +fn plot_range() { + use plotters::prelude::*; + + let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Range Layout", ("sans-serif", 60)).unwrap(); + + let k = 11; + let inputs = [0, 0].map(Fr::from); + let mut builder = GateThreadBuilder::new(false); + range_tests(builder.main(0), 3, inputs, 8, 8); + + // auto-tune circuit + builder.config(k, Some(9)); + // create circuit + let circuit = RangeCircuitBuilder::keygen(builder); + halo2_proofs::dev::CircuitLayout::default().render(7, &circuit, &root).unwrap(); +} diff --git a/halo2-base/src/gates/tests/idx_to_indicator.rs b/halo2-base/src/gates/tests/idx_to_indicator.rs index 3a1e69a5..4520b67c 100644 --- a/halo2-base/src/gates/tests/idx_to_indicator.rs +++ b/halo2-base/src/gates/tests/idx_to_indicator.rs @@ -1,9 +1,16 @@ -use crate::halo2_proofs::{ - plonk::keygen_pk, - plonk::{keygen_vk, Assigned}, - poly::kzg::commitment::ParamsKZG, +use crate::{ + gates::{ + builder::{GateCircuitBuilder, GateThreadBuilder}, + GateChip, GateInstructions, + }, + halo2_proofs::{ + plonk::keygen_pk, + plonk::{keygen_vk, Assigned}, + poly::kzg::commitment::ParamsKZG, + }, }; +use ff::Field; use itertools::Itertools; use rand::{thread_rng, Rng}; diff --git a/halo2-base/src/gates/tests/mod.rs b/halo2-base/src/gates/tests/mod.rs index 07d90351..de6234e7 100644 --- a/halo2-base/src/gates/tests/mod.rs +++ b/halo2-base/src/gates/tests/mod.rs @@ -1,11 +1,6 @@ -use super::builder::{GateCircuitBuilder, GateThreadBuilder, RangeCircuitBuilder}; -use super::flex_gate::{GateChip, GateInstructions}; -use super::range::{RangeChip, RangeInstructions}; use crate::halo2_proofs::{ - dev::MockProver, halo2curves::bn256::{Bn256, Fr, G1Affine}, - plonk::{create_proof, ProvingKey}, - plonk::{verify_proof, VerifyingKey}, + plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, poly::commitment::ParamsProver, poly::kzg::{ commitment::KZGCommitmentScheme, commitment::ParamsKZG, multiopen::ProverSHPLONK, @@ -15,179 +10,18 @@ use crate::halo2_proofs::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }, }; -use crate::utils::{BigPrimeField, ScalarField}; -use crate::{Context, QuantumCell::Constant}; -use ff::Field; use rand::rngs::OsRng; -use rayon::prelude::*; +#[cfg(test)] +mod general; +#[cfg(test)] mod idx_to_indicator; -fn gate_tests(ctx: &mut Context, inputs: [F; 3]) { - let [a, b, c]: [_; 3] = ctx.assign_witnesses(inputs).try_into().unwrap(); - let chip = GateChip::default(); - - // test add - chip.add(ctx, a, b); - - // test sub - chip.sub(ctx, a, b); - - // test multiply - chip.mul(ctx, c, b); - - // test idx_to_indicator - chip.idx_to_indicator(ctx, Constant(F::from(3u64)), 4); - - let bits = ctx.assign_witnesses([F::zero(), F::one()]); - chip.bits_to_indicator(ctx, &bits); - - chip.is_equal(ctx, b, a); - - chip.is_zero(ctx, a); -} - -#[test] -fn test_gates() { - let k = 6; - let inputs = [10u64, 12u64, 120u64].map(Fr::from); - let mut builder = GateThreadBuilder::mock(); - gate_tests(builder.main(0), inputs); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = GateCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); -} - -#[test] -fn test_multithread_gates() { - let k = 6; - let inputs = [10u64, 12u64, 120u64].map(Fr::from); - let mut builder = GateThreadBuilder::mock(); - gate_tests(builder.main(0), inputs); - - let thread_ids = (0..4usize).map(|_| builder.get_new_thread_id()).collect::>(); - let new_threads = thread_ids - .into_par_iter() - .map(|id| { - let mut ctx = Context::new(builder.witness_gen_only(), id); - gate_tests(&mut ctx, [(); 3].map(|_| Fr::random(OsRng))); - ctx - }) - .collect::>(); - builder.threads[0].extend(new_threads); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = GateCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); -} - -#[cfg(feature = "dev-graph")] -#[test] -fn plot_gates() { - let k = 5; - use plotters::prelude::*; - - let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Gates Layout", ("sans-serif", 60)).unwrap(); - - let inputs = [Fr::zero(); 3]; - let builder = GateThreadBuilder::new(false); - gate_tests(builder.main(0), inputs); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = GateCircuitBuilder::keygen(builder); - halo2_proofs::dev::CircuitLayout::default().render(k, &circuit, &root).unwrap(); -} - -fn range_tests( - ctx: &mut Context, - lookup_bits: usize, - inputs: [F; 2], - range_bits: usize, - lt_bits: usize, -) { - let [a, b]: [_; 2] = ctx.assign_witnesses(inputs).try_into().unwrap(); - let chip = RangeChip::default(lookup_bits); - std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); - - chip.range_check(ctx, a, range_bits); - - chip.check_less_than(ctx, a, b, lt_bits); - - chip.is_less_than(ctx, a, b, lt_bits); - - chip.is_less_than(ctx, b, a, lt_bits); - - chip.div_mod(ctx, a, 7u64, lt_bits); -} - -#[test] -fn test_range_single() { - let k = 11; - let inputs = [100, 101].map(Fr::from); - let mut builder = GateThreadBuilder::mock(); - range_tests(builder.main(0), 3, inputs, 8, 8); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = RangeCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); -} - -#[test] -fn test_range_multicolumn() { - let k = 5; - let inputs = [100, 101].map(Fr::from); - let mut builder = GateThreadBuilder::mock(); - range_tests(builder.main(0), 3, inputs, 8, 8); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = RangeCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); -} - -#[cfg(feature = "dev-graph")] -#[test] -fn plot_range() { - use plotters::prelude::*; - - let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Range Layout", ("sans-serif", 60)).unwrap(); - - let k = 11; - let inputs = [0, 0].map(Fr::from); - let mut builder = GateThreadBuilder::new(false); - range_tests(builder.main(0), 3, inputs, 8, 8); - - // auto-tune circuit - builder.config(k, Some(9)); - // create circuit - let circuit = RangeCircuitBuilder::keygen(builder); - halo2_proofs::dev::CircuitLayout::default().render(7, &circuit, &root).unwrap(); -} - // helper functions - pub fn gen_proof( params: &ParamsKZG, pk: &ProvingKey, - circuit: GateCircuitBuilder, + circuit: impl Circuit, ) -> Vec { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); create_proof::< diff --git a/halo2-ecc/Cargo.toml b/halo2-ecc/Cargo.toml index a2b4fe07..d5c9d056 100644 --- a/halo2-ecc/Cargo.toml +++ b/halo2-ecc/Cargo.toml @@ -26,6 +26,7 @@ ark-std = { version = "0.3.0", features = ["print-trace"] } pprof = { version = "0.11", features = ["criterion", "flamegraph"] } criterion = "0.4" criterion-macro = "0.4" +halo2-base = { path = "../halo2-base", default-features = false, features = ["test-utils"] } [features] default = ["jemallocator", "halo2-axiom", "display"] diff --git a/halo2-ecc/src/fields/tests.rs b/halo2-ecc/src/fields/tests.rs deleted file mode 100644 index 6e013486..00000000 --- a/halo2-ecc/src/fields/tests.rs +++ /dev/null @@ -1,153 +0,0 @@ -mod fp { - use crate::fields::fp::FpChip; - use crate::fields::{FieldChip, PrimeField}; - use crate::halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::{Fq, Fr}, - }; - use halo2_base::gates::builder::{GateThreadBuilder, RangeCircuitBuilder}; - use halo2_base::gates::RangeChip; - use halo2_base::utils::biguint_to_fe; - use halo2_base::utils::{fe_to_biguint, modulus}; - use halo2_base::Context; - use rand::rngs::OsRng; - - const K: usize = 10; - - fn fp_mul_test( - ctx: &mut Context, - lookup_bits: usize, - limb_bits: usize, - num_limbs: usize, - _a: Fq, - _b: Fq, - ) { - std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); - let range = RangeChip::::default(lookup_bits); - let chip = FpChip::::new(&range, limb_bits, num_limbs); - - let [a, b] = [_a, _b].map(|x| chip.load_private(ctx, FpChip::::fe_to_witness(&x))); - let c = chip.mul(ctx, &a, &b); - - assert_eq!(c.truncation.to_bigint(limb_bits), c.value); - assert_eq!( - c.native.value(), - &biguint_to_fe(&(&c.value.to_biguint().unwrap() % modulus::())) - ); - assert_eq!(c.value, fe_to_biguint(&(_a * _b)).into()) - } - - #[test] - fn test_fp() { - let k = K; - let a = Fq::random(OsRng); - let b = Fq::random(OsRng); - - let mut builder = GateThreadBuilder::::mock(); - fp_mul_test(builder.main(0), k - 1, 88, 3, a, b); - - builder.config(k, Some(10)); - let circuit = RangeCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); - } - - #[cfg(feature = "dev-graph")] - #[test] - fn plot_fp() { - use plotters::prelude::*; - - let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Fp Layout", ("sans-serif", 60)).unwrap(); - - let k = K; - let a = Fq::zero(); - let b = Fq::zero(); - - let mut builder = GateThreadBuilder::keygen(); - fp_mul_test(builder.main(0), k - 1, 88, 3, a, b); - - builder.config(k, Some(10)); - let circuit = RangeCircuitBuilder::keygen(builder); - halo2_proofs::dev::CircuitLayout::default().render(k as u32, &circuit, &root).unwrap(); - } -} - -mod fp12 { - use crate::fields::fp::FpChip; - use crate::fields::fp12::Fp12Chip; - use crate::fields::{FieldChip, PrimeField}; - use crate::halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::{Fq, Fq12, Fr}, - }; - use halo2_base::gates::builder::{GateThreadBuilder, RangeCircuitBuilder}; - use halo2_base::gates::RangeChip; - use halo2_base::Context; - use rand_core::OsRng; - - const XI_0: i64 = 9; - - fn fp12_mul_test( - ctx: &mut Context, - lookup_bits: usize, - limb_bits: usize, - num_limbs: usize, - _a: Fq12, - _b: Fq12, - ) { - std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); - let range = RangeChip::::default(lookup_bits); - let fp_chip = FpChip::::new(&range, limb_bits, num_limbs); - let chip = Fp12Chip::::new(&fp_chip); - - let [a, b] = [_a, _b].map(|x| { - chip.load_private(ctx, Fp12Chip::, Fq12, XI_0>::fe_to_witness(&x)) - }); - let c = chip.mul(ctx, &a, &b); - - assert_eq!(chip.get_assigned_value(&c), _a * _b); - for c in c.coeffs { - assert_eq!(c.truncation.to_bigint(limb_bits), c.value); - } - } - - #[test] - fn test_fp12() { - let k = 12; - let a = Fq12::random(OsRng); - let b = Fq12::random(OsRng); - - let mut builder = GateThreadBuilder::::mock(); - fp12_mul_test(builder.main(0), k - 1, 88, 3, a, b); - - builder.config(k, Some(20)); - let circuit = RangeCircuitBuilder::mock(builder); - - MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); - } - - #[cfg(feature = "dev-graph")] - #[test] - fn plot_fp12() { - use ff::Field; - use plotters::prelude::*; - - let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root.titled("Fp Layout", ("sans-serif", 60)).unwrap(); - - let k = 23; - let a = Fq12::zero(); - let b = Fq12::zero(); - - let mut builder = GateThreadBuilder::::mock(); - fp12_mul_test(builder.main(0), k - 1, 88, 3, a, b); - - builder.config(k, Some(20)); - let circuit = RangeCircuitBuilder::mock(builder); - - halo2_proofs::dev::CircuitLayout::default().render(k, &circuit, &root).unwrap(); - } -} diff --git a/halo2-ecc/src/fields/tests/fp/assert_eq.rs b/halo2-ecc/src/fields/tests/fp/assert_eq.rs new file mode 100644 index 00000000..ef45aa99 --- /dev/null +++ b/halo2-ecc/src/fields/tests/fp/assert_eq.rs @@ -0,0 +1,85 @@ +use std::env::set_var; + +use ff::Field; +use halo2_base::{ + gates::{ + builder::{GateThreadBuilder, RangeCircuitBuilder}, + tests::{check_proof, gen_proof}, + RangeChip, + }, + halo2_proofs::{ + halo2curves::bn256::{Fq, Fr}, + plonk::keygen_pk, + plonk::keygen_vk, + poly::kzg::commitment::ParamsKZG, + }, +}; +use num_bigint::BigInt; + +use crate::{bn254::FpChip, fields::FieldChip}; +use rand::thread_rng; + +// soundness checks for `` function +fn test_fp_assert_eq_gen(k: u32, lookup_bits: usize, num_tries: usize) { + let mut rng = thread_rng(); + set_var("LOOKUP_BITS", lookup_bits.to_string()); + + // first create proving and verifying key + let mut builder = GateThreadBuilder::keygen(); + let range = RangeChip::default(lookup_bits); + let chip = FpChip::new(&range, 88, 3); + + let ctx = builder.main(0); + let a = chip.load_private(ctx, BigInt::from(0)); + let b = chip.load_private(ctx, BigInt::from(0)); + chip.assert_equal(ctx, &a, &b); + // set env vars + builder.config(k as usize, Some(9)); + let circuit = RangeCircuitBuilder::keygen(builder); + + let params = ParamsKZG::setup(k, &mut rng); + // 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 = |a: Fq, b: Fq| { + let mut builder = GateThreadBuilder::prover(); + let range = RangeChip::default(lookup_bits); + let chip = FpChip::new(&range, 88, 3); + + let ctx = builder.main(0); + let [a, b] = [a, b].map(|x| chip.load_private(ctx, FpChip::::fe_to_witness(&x))); + chip.assert_equal(ctx, &a, &b); + let circuit = RangeCircuitBuilder::prover(builder, vec![vec![]]); // no break points + gen_proof(¶ms, &pk, circuit) + }; + + // expected answer + for _ in 0..num_tries { + let a = Fq::random(&mut rng); + let pf = gen_pf(a, a); + check_proof(¶ms, vk, &pf, true); + } + + // unequal + for _ in 0..num_tries { + let a = Fq::random(&mut rng); + let b = Fq::random(&mut rng); + if a == b { + continue; + } + let pf = gen_pf(a, b); + check_proof(¶ms, vk, &pf, false); + } +} + +#[test] +fn test_fp_assert_eq() { + test_fp_assert_eq_gen(10, 4, 100); + test_fp_assert_eq_gen(10, 8, 100); + test_fp_assert_eq_gen(10, 9, 100); + test_fp_assert_eq_gen(18, 17, 10); +} diff --git a/halo2-ecc/src/fields/tests/fp/mod.rs b/halo2-ecc/src/fields/tests/fp/mod.rs new file mode 100644 index 00000000..a4dfb24e --- /dev/null +++ b/halo2-ecc/src/fields/tests/fp/mod.rs @@ -0,0 +1,75 @@ +use crate::fields::fp::FpChip; +use crate::fields::{FieldChip, PrimeField}; +use crate::halo2_proofs::{ + dev::MockProver, + halo2curves::bn256::{Fq, Fr}, +}; +use halo2_base::gates::builder::{GateThreadBuilder, RangeCircuitBuilder}; +use halo2_base::gates::RangeChip; +use halo2_base::utils::biguint_to_fe; +use halo2_base::utils::{fe_to_biguint, modulus}; +use halo2_base::Context; +use rand::rngs::OsRng; + +pub mod assert_eq; + +const K: usize = 10; + +fn fp_mul_test( + ctx: &mut Context, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, + _a: Fq, + _b: Fq, +) { + std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); + let range = RangeChip::::default(lookup_bits); + let chip = FpChip::::new(&range, limb_bits, num_limbs); + + let [a, b] = [_a, _b].map(|x| chip.load_private(ctx, FpChip::::fe_to_witness(&x))); + let c = chip.mul(ctx, &a, &b); + + assert_eq!(c.truncation.to_bigint(limb_bits), c.value); + assert_eq!( + c.native.value(), + &biguint_to_fe(&(&c.value.to_biguint().unwrap() % modulus::())) + ); + assert_eq!(c.value, fe_to_biguint(&(_a * _b)).into()) +} + +#[test] +fn test_fp() { + let k = K; + let a = Fq::random(OsRng); + let b = Fq::random(OsRng); + + let mut builder = GateThreadBuilder::::mock(); + fp_mul_test(builder.main(0), k - 1, 88, 3, a, b); + + builder.config(k, Some(10)); + let circuit = RangeCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[cfg(feature = "dev-graph")] +#[test] +fn plot_fp() { + use plotters::prelude::*; + + let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Fp Layout", ("sans-serif", 60)).unwrap(); + + let k = K; + let a = Fq::zero(); + let b = Fq::zero(); + + let mut builder = GateThreadBuilder::keygen(); + fp_mul_test(builder.main(0), k - 1, 88, 3, a, b); + + builder.config(k, Some(10)); + let circuit = RangeCircuitBuilder::keygen(builder); + halo2_proofs::dev::CircuitLayout::default().render(k as u32, &circuit, &root).unwrap(); +} diff --git a/halo2-ecc/src/fields/tests/fp12/mod.rs b/halo2-ecc/src/fields/tests/fp12/mod.rs new file mode 100644 index 00000000..7112d690 --- /dev/null +++ b/halo2-ecc/src/fields/tests/fp12/mod.rs @@ -0,0 +1,75 @@ +use crate::fields::fp::FpChip; +use crate::fields::fp12::Fp12Chip; +use crate::fields::{FieldChip, PrimeField}; +use crate::halo2_proofs::{ + dev::MockProver, + halo2curves::bn256::{Fq, Fq12, Fr}, +}; +use halo2_base::gates::builder::{GateThreadBuilder, RangeCircuitBuilder}; +use halo2_base::gates::RangeChip; +use halo2_base::Context; +use rand_core::OsRng; + +const XI_0: i64 = 9; + +fn fp12_mul_test( + ctx: &mut Context, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, + _a: Fq12, + _b: Fq12, +) { + std::env::set_var("LOOKUP_BITS", lookup_bits.to_string()); + let range = RangeChip::::default(lookup_bits); + let fp_chip = FpChip::::new(&range, limb_bits, num_limbs); + let chip = Fp12Chip::::new(&fp_chip); + + let [a, b] = [_a, _b].map(|x| { + chip.load_private(ctx, Fp12Chip::, Fq12, XI_0>::fe_to_witness(&x)) + }); + let c = chip.mul(ctx, &a, &b); + + assert_eq!(chip.get_assigned_value(&c), _a * _b); + for c in c.coeffs { + assert_eq!(c.truncation.to_bigint(limb_bits), c.value); + } +} + +#[test] +fn test_fp12() { + let k = 12; + let a = Fq12::random(OsRng); + let b = Fq12::random(OsRng); + + let mut builder = GateThreadBuilder::::mock(); + fp12_mul_test(builder.main(0), k - 1, 88, 3, a, b); + + builder.config(k, Some(20)); + let circuit = RangeCircuitBuilder::mock(builder); + + MockProver::run(k as u32, &circuit, vec![]).unwrap().assert_satisfied(); +} + +#[cfg(feature = "dev-graph")] +#[test] +fn plot_fp12() { + use ff::Field; + use plotters::prelude::*; + + let root = BitMapBackend::new("layout.png", (1024, 1024)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Fp Layout", ("sans-serif", 60)).unwrap(); + + let k = 23; + let a = Fq12::zero(); + let b = Fq12::zero(); + + let mut builder = GateThreadBuilder::::mock(); + fp12_mul_test(builder.main(0), k - 1, 88, 3, a, b); + + builder.config(k, Some(20)); + let circuit = RangeCircuitBuilder::mock(builder); + + halo2_proofs::dev::CircuitLayout::default().render(k, &circuit, &root).unwrap(); +} diff --git a/halo2-ecc/src/fields/tests/mod.rs b/halo2-ecc/src/fields/tests/mod.rs new file mode 100644 index 00000000..460ae96a --- /dev/null +++ b/halo2-ecc/src/fields/tests/mod.rs @@ -0,0 +1,2 @@ +pub mod fp; +pub mod fp12;