From f43a275fc1a64b706fe02c68a2f357396f16d1ec Mon Sep 17 00:00:00 2001 From: Oyun <1340839769@qq.com> Date: Wed, 28 Aug 2024 19:20:03 +0800 Subject: [PATCH] feat: something about shuffle and msp !! cannot run --- halo2-base/Cargo.toml | 3 +- halo2-base/src/gates/circuit/builder.rs | 2 +- halo2-base/src/gates/circuit/mod.rs | 14 + halo2-base/src/lib.rs | 1 + halo2-base/src/utils/halo2.rs | 2 +- halo2-ecc/Cargo.toml | 4 +- halo2-ecc/configs/bn254/shuffle.config | 2 + halo2-ecc/src/bn254/mod.rs | 3 + halo2-ecc/src/bn254/msp.rs | 103 +++++ halo2-ecc/src/bn254/shuffle.rs | 423 +++++++++++++++++++++ halo2-ecc/src/bn254/shuffle2.rs | 52 +++ halo2-ecc/src/bn254/tests/bls_signature.rs | 8 +- halo2-ecc/src/bn254/tests/mod.rs | 1 + halo2-ecc/src/bn254/tests/msp.rs | 168 ++++++++ halo2-ecc/src/lib.rs | 6 +- hashes/zkevm/Cargo.toml | 3 +- rust-toolchain | 2 +- 17 files changed, 784 insertions(+), 13 deletions(-) create mode 100644 halo2-ecc/configs/bn254/shuffle.config create mode 100644 halo2-ecc/src/bn254/msp.rs create mode 100644 halo2-ecc/src/bn254/shuffle.rs create mode 100644 halo2-ecc/src/bn254/shuffle2.rs create mode 100644 halo2-ecc/src/bn254/tests/msp.rs diff --git a/halo2-base/Cargo.toml b/halo2-base/Cargo.toml index 5286ee9..db4f4d2 100644 --- a/halo2-base/Cargo.toml +++ b/halo2-base/Cargo.toml @@ -57,7 +57,8 @@ jemallocator = { version = "=0.5", optional = true } mimalloc = { version = "=0.1", default-features = false, optional = true } [features] -default = ["halo2-axiom", "display", "test-utils"] +# default = ["halo2-axiom", "display", "test-utils"] +default = ["halo2-pse", "display", "test-utils"] asm = ["halo2_proofs_axiom?/asm"] dev-graph = ["halo2_proofs?/dev-graph", "plotters"] # only works with halo2-pse for now halo2-pse = ["halo2_proofs/circuit-params"] diff --git a/halo2-base/src/gates/circuit/builder.rs b/halo2-base/src/gates/circuit/builder.rs index ae7e188..d121977 100644 --- a/halo2-base/src/gates/circuit/builder.rs +++ b/halo2-base/src/gates/circuit/builder.rs @@ -302,7 +302,7 @@ impl BaseCircuitBuilder { let copy_manager = self.core.copy_manager.lock().unwrap(); let cell = copy_manager.assigned_advices.get(&cell).expect("instance not assigned"); - layouter.constrain_instance(*cell, *instance_col, i); + let _ = layouter.constrain_instance(*cell, *instance_col, i); } } } diff --git a/halo2-base/src/gates/circuit/mod.rs b/halo2-base/src/gates/circuit/mod.rs index 8b3e6f6..713104f 100644 --- a/halo2-base/src/gates/circuit/mod.rs +++ b/halo2-base/src/gates/circuit/mod.rs @@ -1,3 +1,4 @@ +// use proptest::strategy::W; use serde::{Deserialize, Serialize}; use crate::utils::ScalarField; @@ -13,6 +14,7 @@ use self::builder::BaseCircuitBuilder; use super::flex_gate::{FlexGateConfig, FlexGateConfigParams}; use super::range::RangeConfig; +// use super::shuffle::ShuffleConfig; /// Module that helps auto-build circuits pub mod builder; @@ -67,6 +69,15 @@ pub enum MaybeRangeConfig { WithRange(RangeConfig), } + +// #[derive(Clone, Debug)] +// pub enum MaybeShuffleConfig { +// /// Config for a circuit that does not use range checks +// WithoutShuffle(FlexGateConfig), +// /// Config for a circuit that does use range checks +// WithShuffle(ShuffleConfig), +// } + impl BaseConfig { /// Generates a new `BaseConfig` depending on `params`. /// - It will generate a `RangeConfig` is `params` has `lookup_bits` not None **and** `num_lookup_advice_per_phase` are not all empty or zero (i.e., if `params` indicates that the circuit actually requires a lookup table). @@ -170,6 +181,9 @@ impl Circuit for BaseCircuitBuilder { if let MaybeRangeConfig::WithRange(config) = &config.base { config.load_lookup_table(&mut layouter).expect("load lookup table should not fail"); } + // if let MaybeShuffleConfig::WithShuffle(config) = &config.base { + // config.load_shuffle(&mut layouter).expect("load shuffle should not fail"); + // } // Only FirstPhase (phase 0) layouter .assign_region( diff --git a/halo2-base/src/lib.rs b/halo2-base/src/lib.rs index ac2a322..e981a91 100644 --- a/halo2-base/src/lib.rs +++ b/halo2-base/src/lib.rs @@ -9,6 +9,7 @@ #![warn(clippy::default_numeric_fallback)] #![warn(missing_docs)] + use getset::CopyGetters; use itertools::Itertools; // Different memory allocator options: diff --git a/halo2-base/src/utils/halo2.rs b/halo2-base/src/utils/halo2.rs index 750787f..1ede74c 100644 --- a/halo2-base/src/utils/halo2.rs +++ b/halo2-base/src/utils/halo2.rs @@ -100,7 +100,7 @@ pub fn constrain_virtual_equals_external( match copy_manager.assigned_advices.entry(ctx_cell) { Entry::Occupied(acell) => { // The virtual cell has already been assigned, so we can constrain it to equal the external cell. - region.constrain_equal(*acell.get(), external_cell); + let _ = region.constrain_equal(*acell.get(), external_cell); } Entry::Vacant(assigned) => { // The virtual cell **must** be an external cell diff --git a/halo2-ecc/Cargo.toml b/halo2-ecc/Cargo.toml index 4ba2aab..06457f7 100644 --- a/halo2-ecc/Cargo.toml +++ b/halo2-ecc/Cargo.toml @@ -27,6 +27,7 @@ halo2-base = { version = "=0.4.1", path = "../halo2-base", default-features = fa # plotting circuit layout plotters = { version = "0.3.0", optional = true } hex = "0.4.3" +poseidon = {git = "https://github.com/scroll-tech/poseidon"} [dev-dependencies] ark-std = { version = "0.3.0", features = ["print-trace"] } @@ -38,7 +39,8 @@ test-log = "0.2.12" env_logger = "0.10.0" [features] -default = ["jemallocator", "halo2-axiom", "display"] +# default = ["jemallocator", "halo2-axiom", "display"] +default = ["jemallocator", "halo2-pse", "display"] dev-graph = ["halo2-base/dev-graph", "plotters"] display = ["halo2-base/display"] asm = ["halo2-base/asm"] diff --git a/halo2-ecc/configs/bn254/shuffle.config b/halo2-ecc/configs/bn254/shuffle.config new file mode 100644 index 0000000..f469705 --- /dev/null +++ b/halo2-ecc/configs/bn254/shuffle.config @@ -0,0 +1,2 @@ +{"strategy":"Simple","degree":14,"num_aggregation":2} +{"strategy":"Simple","degree":15,"num_aggregation":2} diff --git a/halo2-ecc/src/bn254/mod.rs b/halo2-ecc/src/bn254/mod.rs index cc6bd71..c8553fe 100644 --- a/halo2-ecc/src/bn254/mod.rs +++ b/halo2-ecc/src/bn254/mod.rs @@ -9,6 +9,9 @@ pub mod final_exp; pub mod pairing; pub mod merkle_tree; pub mod combine_bls_mt; +pub mod shuffle; +pub mod msp; +pub mod shuffle2; #[derive(Clone)] pub struct MerkleInfo{ diff --git a/halo2-ecc/src/bn254/msp.rs b/halo2-ecc/src/bn254/msp.rs new file mode 100644 index 0000000..d66ef21 --- /dev/null +++ b/halo2-ecc/src/bn254/msp.rs @@ -0,0 +1,103 @@ +#![allow(non_snake_case)] + +use super::bls_signature::BlsSignatureChip; +use super::pairing::PairingChip; +use super::{Fp12Chip, Fp2Chip, FpChip}; +use crate::bigint::ProperCrtUint; +use crate::ecc::{scalar_multiply, EcPoint, EccChip}; +use crate::fields::vector::{FieldVector, CRTInteger}; +use crate::fields::{fp, fp12, fp2, FieldChip}; +use crate::halo2_proofs::halo2curves::bn256::Fq12; +use crate::halo2_proofs::halo2curves::bn256::{G1Affine, G2Affine}; +use halo2_base::gates::{GateChip,GateInstructions}; +use halo2_base::halo2_proofs::halo2curves::bn256::{Fq, Fq2}; +use halo2_base::poseidon::hasher::PoseidonHasher; +use halo2_base::utils::BigPrimeField; +use halo2_base::{AssignedValue, Context}; + +// To avoid issues with mutably borrowing twice (not allowed in Rust), we only store fp_chip and construct g2_chip and fp12_chip in scope when needed for temporary mutable borrows +pub struct MspChip<'chip, F: BigPrimeField> { + pub bls_signature_chip: &'chip BlsSignatureChip<'chip, F>, + pub poseidon_chip: &'chip PoseidonHasher, + // pub fp_chip: &'chip FpChip<'chip, F>, +} + +impl<'chip, F: BigPrimeField> MspChip<'chip, F> { + pub fn new( + bls_signature_chip: &'chip BlsSignatureChip, + poseidon_chip: &'chip PoseidonHasher, + // fp_chip: &'chip FpChip, + ) -> Self { + Self { + bls_signature_chip, + poseidon_chip, + // fp_chip, + } + } + + pub fn msp_verify( + &self, + ctx: &mut Context, + g1: G1Affine, + signatures: &[G2Affine], + pubkeys: &[G1Affine], // mvk + msghash: G2Affine, + weighting_seed : F, + ivk: G1Affine, + isig: G2Affine, // \mu + ) -> AssignedValue { + // TODO: verify proof of possesion + + // A: verify BLS signature + let verify_A = self.bls_signature_chip.bls_signature_verify(ctx, g1, signatures, pubkeys, msghash); + + // B + let signatures_x_assigned = signatures.iter().map(|pt| { + ctx.load_witness(F::from_bytes_le(&pt.x.c0.to_bytes())) + }).collect::>(); + let gate_chip = GateChip::::default(); + let weighting_seed_comp = self.poseidon_chip.hash_fix_len_array(ctx, &gate_chip, &signatures_x_assigned[..]); + let weighting_seed_assigned = ctx.load_witness(weighting_seed); + // B_1 : verify weighting seed + let verify_B_1 = gate_chip.is_equal(ctx, weighting_seed_assigned, weighting_seed_comp); + // e_i = H(i,weighting_seed) for i in 0..n where n is the number of public keys + let e_is = pubkeys.iter().enumerate().map(|(i, _)| { + let i_assigned = ctx.load_witness(F::from(i as u64)); + self.poseidon_chip.hash_fix_len_array(ctx, &gate_chip, &[i_assigned, weighting_seed_assigned]) + }).collect::>(); + + let g1_chip = EccChip::new(self.bls_signature_chip.fp_chip); + let fp2_chip = Fp2Chip::new(self.bls_signature_chip.fp_chip); + let g2_chip = EccChip::new(&fp2_chip); + // B_2 : verify ivk, isig + // ivk = \sum_{i=0}^{n-1} e_i * mvk_i + let ivk_assigned = self.bls_signature_chip.pairing_chip.load_private_g1(ctx, ivk); + let mvks = pubkeys.iter().map(|pt| self.bls_signature_chip.pairing_chip.load_private_g1(ctx, *pt)).collect::>(); + let products = mvks.iter().zip(e_is.iter()).map(|(mvk, e_i)| { + g1_chip.scalar_mult(ctx, mvk.clone(), e_is.clone(),64,12) + }).collect::>(); + let ivk_comp = g1_chip.sum(ctx, products); + let verify_B_2 = g1_chip.is_equal(ctx, ivk_assigned, ivk_comp); + // isig = \sum_{i=0}^{n-1} e_i * sig_i + let isig_assigned = self.bls_signature_chip.pairing_chip.load_private_g2(ctx, isig); + let sigs = signatures.iter().map(|pt| self.bls_signature_chip.pairing_chip.load_private_g2(ctx, *pt)).collect::>(); + let products = sigs.iter().zip(e_is.iter()).map(|(sig, e_i)| { + g2_chip.scalar_mult(ctx, sig.clone(), e_is.clone(),64,12) + }).collect::>(); + let isig_comp = g2_chip.sum(ctx, products); + + let verify_B_3 = g2_chip.is_equal(ctx, isig_assigned, isig_comp); + + // B_4 : verify e(g1, isig) = e(ivk, H(m)) + let verify_B_4 = self.bls_signature_chip.bls_signature_verify(ctx, g1, &[isig], &[ivk], msghash); + + // Final result + let result = gate_chip.and( + ctx, + &gate_chip.and(ctx, &verify_A, &verify_B_1), + &gate_chip.and(ctx, &verify_B_2, &gate_chip.and(ctx, &verify_B_3, &verify_B_4)), + ); + result + + } +} diff --git a/halo2-ecc/src/bn254/shuffle.rs b/halo2-ecc/src/bn254/shuffle.rs new file mode 100644 index 0000000..3316895 --- /dev/null +++ b/halo2-ecc/src/bn254/shuffle.rs @@ -0,0 +1,423 @@ +use halo2_base::{halo2_proofs::{ + arithmetic::{CurveAffine, Field}, circuit::{floor_planner::V1, Layouter, Value}, dev::{metadata, FailureLocation, MockProver, VerifyFailure}, halo2curves::{bn256::{Bn256, Fr, G1Affine}, pasta::EqAffine}, plonk::*, poly::{ + commitment::ParamsProver, ipa::{ + commitment::{IPACommitmentScheme, ParamsIPA}, + multiopen::{ProverIPA, VerifierIPA}, + strategy::AccumulatorStrategy, + }, kzg::{commitment::{KZGCommitmentScheme, ParamsKZG}, multiopen::{ProverSHPLONK, VerifierSHPLONK}, strategy::SingleStrategy}, VerificationStrategy + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + } +}, utils::fs::gen_srs}; +use rand::rngs::StdRng; +use rayon::result; +use crate::{ff::{BatchInvert, FromUniformBytes}, fields::FpStrategy}; +use rand_core::{RngCore, SeedableRng}; +use rand_chacha::ChaCha20Rng; +use std::{io::BufRead, iter, time::Duration}; +use std::io::Write; + + +fn test_rng() -> ChaCha20Rng { + ChaCha20Rng::seed_from_u64(0xdeadbeef) +} + +#[derive(Clone, Copy, Debug)] +#[derive(serde::Deserialize)] +struct ShuffleCircuitParams { + strategy: FpStrategy, + degree: u32, + num_aggregation: u32, +} + + + +fn rand_2d_array(rng: &mut R) -> [[F; H]; W] { + [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng))) +} + +fn shuffled( + original: [[F; H]; W], + rng: &mut R, +) -> [[F; H]; W] { + let mut shuffled = original; + // println!("{:?}",original); + for row in (1..H).rev() { + let rand_row = (rng.next_u32() as usize) % row; + for column in shuffled.iter_mut() { + column.swap(row, rand_row); + } + } + // println!("{:?}",shuffled); + shuffled +} + + + +#[derive(Clone)] +struct MyConfig { + q_shuffle: Selector, + q_first: Selector, + q_last: Selector, + original: [Column; W], + shuffled: [Column; W], + theta: Challenge, + gamma: Challenge, + z: Column, +} + +impl MyConfig { + fn configure(meta: &mut ConstraintSystem) -> Self { + let [q_shuffle, q_first, q_last] = [(); 3].map(|_| meta.selector()); + // First phase + let original = [(); W].map(|_| meta.advice_column_in(FirstPhase)); + let shuffled = [(); W].map(|_| meta.advice_column_in(FirstPhase)); + let [theta, gamma] = [(); 2].map(|_| meta.challenge_usable_after(FirstPhase)); + // Second phase + let z = meta.advice_column_in(SecondPhase); + + meta.create_gate("z should start with 1", |_| { + let one = Expression::Constant(F::ONE); + + vec![q_first.expr() * (one - z.cur())] + }); + + meta.create_gate("z should end with 1", |_| { + let one = Expression::Constant(F::ONE); + + vec![q_last.expr() * (one - z.cur())] + }); + + meta.create_gate("z should have valid transition", |_| { + let q_shuffle = q_shuffle.expr(); + let original = original.map(|advice| advice.cur()); + let shuffled = shuffled.map(|advice| advice.cur()); + let [theta, gamma] = [theta, gamma].map(|challenge| challenge.expr()); + + // Compress + let original = original + .iter() + .cloned() + .reduce(|acc, a| acc * theta.clone() + a) + .unwrap(); + let shuffled = shuffled + .iter() + .cloned() + .reduce(|acc, a| acc * theta.clone() + a) + .unwrap(); + + vec![q_shuffle * (z.cur() * (original + gamma.clone()) - z.next() * (shuffled + gamma))] + }); + + Self { + q_shuffle, + q_first, + q_last, + original, + shuffled, + theta, + gamma, + z, + } + } +} + +#[derive(Clone, Default)] +struct MyCircuit { + original: Value<[[F; H]; W]>, + shuffled: Value<[[F; H]; W]>, +} + +impl MyCircuit { + fn rand(rng: &mut R) -> Self { + let original = rand_2d_array::(rng); + let shuffled = shuffled(original, rng); + + Self { + original: Value::known(original), + shuffled: Value::known(shuffled), + } + } +} + +impl Circuit for MyCircuit { + type Config = MyConfig; + type FloorPlanner = V1; + // #[cfg(feature = "circuit-params")] + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + MyConfig::configure(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let theta = layouter.get_challenge(config.theta); + let gamma = layouter.get_challenge(config.gamma); + + layouter.assign_region( + || "Shuffle original into shuffled", + |mut region| { + // Keygen + config.q_first.enable(&mut region, 0)?; + config.q_last.enable(&mut region, H)?; + for offset in 0..H { + config.q_shuffle.enable(&mut region, offset)?; + } + + // First phase + for (idx, (&column, values)) in config + .original + .iter() + .zip(self.original.transpose_array().iter()) + .enumerate() + { + for (offset, &value) in values.transpose_array().iter().enumerate() { + region.assign_advice( + || format!("original[{idx}][{offset}]"), + column, + offset, + || value, + )?; + } + } + for (idx, (&column, values)) in config + .shuffled + .iter() + .zip(self.shuffled.transpose_array().iter()) + .enumerate() + { + for (offset, &value) in values.transpose_array().iter().enumerate() { + region.assign_advice( + || format!("shuffled[{idx}][{offset}]"), + column, + offset, + || value, + )?; + } + } + + // Second phase + let z = self.original.zip(self.shuffled).zip(theta).zip(gamma).map( + |(((original, shuffled), theta), gamma)| { + let mut product = vec![F::ZERO; H]; + for (idx, product) in product.iter_mut().enumerate() { + let mut compressed = F::ZERO; + for value in shuffled.iter() { + compressed *= theta; + compressed += value[idx]; + } + + *product = compressed + gamma + } + + product.iter_mut().batch_invert(); + + for (idx, product) in product.iter_mut().enumerate() { + let mut compressed = F::ZERO; + for value in original.iter() { + compressed *= theta; + compressed += value[idx]; + } + + *product *= compressed + gamma + } + + #[allow(clippy::let_and_return)] + let z = iter::once(F::ONE) + .chain(product) + .scan(F::ONE, |state, cur| { + *state *= &cur; + Some(*state) + }) + .collect::>(); + + // #[cfg(feature = "sanity-checks")] + // assert_eq!(F::ONE, *z.last().unwrap()); + + z + }, + ); + for (offset, value) in z.transpose_vec(H + 1).into_iter().enumerate() { + region.assign_advice(|| format!("z[{offset}]"), config.z, offset, || value)?; + } + + Ok(()) + }, + ) + } + + +} + +fn test_mock_prover, const W: usize, const H: usize>( + k: u32, + circuit: MyCircuit, + expected: Result<(), Vec<(metadata::Constraint, FailureLocation)>>, +) { + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + match (prover.verify(), expected) { + (Ok(_), Ok(_)) => {} + (Err(err), Err(expected)) => { + assert_eq!( + err.into_iter() + .map(|failure| match failure { + VerifyFailure::ConstraintNotSatisfied { + constraint, + location, + .. + } => (constraint, location), + _ => panic!("MockProver::verify has result unmatching expected"), + }) + .collect::>(), + expected + ) + } + (_, _) => panic!("MockProver::verify has result unmatching expected"), + }; +} +pub fn check_proof( + params: &ParamsKZG, + vk: &VerifyingKey, + proof: &[u8], + instances: &[&[Fr]], + 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, &[instances], &mut transcript); + // Just FYI, because strategy is `SingleStrategy`, the output `res` is `Result<(), Error>`, so there is no need to call `res.finalize()`. + + if expect_satisfied { + res.unwrap(); + } else { + assert!(res.is_err()); + } +} + +struct BenchStats{ + /// Vkey gen time + pub vk_time: Duration, + /// Pkey gen time + pub pk_time: Duration, + /// Proving time + pub proof_time: Duration, + /// Proof size in bytes + pub proof_size: usize, + /// Verify time + pub verify_time: Duration, +} + +fn test_prover( + k: u32, + circuit: impl Circuit, + // circuit: MyCircuit, + expected: bool, +) -> BenchStats +where + C::Scalar: FromUniformBytes<64>, +{ + // let rng = test_rng(); + + let params = gen_srs(k); + + let vk_time = std::time::Instant::now(); + let vk = keygen_vk(¶ms, &circuit).unwrap(); + let vk_duration = vk_time.elapsed(); + println!("vk_duration: {:?}", vk_duration); + + let pk_time = std::time::Instant::now(); + let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + let pk_duration = pk_time.elapsed(); + println!("pk_duration: {:?}", pk_duration); + + let proof_time = std::time::Instant::now(); + let proof = { + let rng = StdRng::seed_from_u64(0); + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof::< + KZGCommitmentScheme, + ProverSHPLONK<'_, Bn256>, + Challenge255<_>, + _, + Blake2bWrite, G1Affine, _>, + _, + >(¶ms, &pk, &[circuit], &[&[]], rng, &mut transcript) + .expect("prover should not fail"); + transcript.finalize() + }; + let proof_duration = proof_time.elapsed(); + println!("proof_duration: {:?}", proof_duration); + + let proof_size = proof.len(); + + let verify_time = std::time::Instant::now(); + check_proof(¶ms, pk.get_vk(), &proof, &[], expected); + let verify_duration = verify_time.elapsed(); + println!("verify_duration: {:?}", verify_duration); + + + BenchStats { + vk_time: vk_duration, + pk_time: pk_duration, + proof_time: proof_duration, + proof_size, + verify_time: verify_duration, + } +} + +#[test] +fn test_shuffle() { + const W: usize = 2; + const H: usize = 1024; + const K: u32 = 18; + + let config_path = "configs/bn254/shuffle.config"; + let bench_params_file = + std::fs::File::open(config_path).unwrap_or_else(|e| panic!("{config_path} does not exist: {e:?}")); + let bench_params_reader = std::io::BufReader::new(bench_params_file); + + let results_path = "results/bn254/shuffle_bench.csv"; + let mut fs_results = std::fs::File::create(results_path).unwrap(); + writeln!(fs_results, "degree,num_aggregation,proof_time,proof_size,verify_time").unwrap(); + + // const H : usize = 2048; + + for line in bench_params_reader.lines() { + let bench_params: ShuffleCircuitParams = + serde_json::from_str(line.unwrap().as_str()).unwrap(); + let k = bench_params.degree; + // let k = 16; + + + let circuit = &MyCircuit::<_, W, H>::rand(&mut test_rng()); + + test_mock_prover(k, circuit.clone(), Ok(())); + + let proof = test_prover::(k, circuit.clone(), true); + writeln!( + fs_results, + "{:?},{},{:?}", + proof.proof_time, + proof.proof_size, + proof.verify_time + ); + () + } + +} diff --git a/halo2-ecc/src/bn254/shuffle2.rs b/halo2-ecc/src/bn254/shuffle2.rs new file mode 100644 index 0000000..3a70353 --- /dev/null +++ b/halo2-ecc/src/bn254/shuffle2.rs @@ -0,0 +1,52 @@ +// use halo2_base::{gates::{GateChip, GateInstructions}, poseidon::PoseidonChip, utils::BigPrimeField, AssignedValue, Context}; + + + + +// pub struct ShuffleChip<'chip, F: BigPrimeField> { +// pub poseidon_chip: PoseidonChip<'chip, F, 3, 2>, +// pub gate_chip: GateChip, +// } + +// impl<'chip, F: BigPrimeField> ShuffleChip<'chip, F>{ +// pub fn new(poseidon_chip: PoseidonChip<'chip, F, 3, 2>, gate_chip: GateChip) -> Self { +// Self { poseidon_chip, gate_chip } +// } + +// pub fn shuffle_verify( +// &self, +// ctx: &mut Context, +// original: Vec, +// shuffled: Vec, +// permutation: Vec, +// ) -> AssignedValue { +// assert_eq!(original.len(), shuffled.len()); +// assert_eq!(original.len(), permutation.len()); + +// let original = original +// .into_iter() +// .map(|x| ctx.load_witness(x)) +// .collect::>(); +// let shuffled = shuffled +// .into_iter() +// .map(|x| ctx.load_witness(x)) +// .collect::>(); +// let permutation = permutation +// .into_iter() +// .map(|x| ctx.load_witness(F::from(x as u64))) +// .collect::>(); + +// let mut shuffled = shuffled; +// for i in 0..original.len() { +// let index = permutation[i]; +// let original_i = original[i]; +// let shuffled_i = shuffled.pop().unwrap(); +// shuffled.push(shuffled_i); +// } + +// let shuffled_i = shuffled.pop().unwrap(); +// let zero = ctx.load_witness(F::ZERO); +// let result = self.gate_chip.is_equal(ctx, shuffled_i, zero); +// result +// } +// } \ No newline at end of file diff --git a/halo2-ecc/src/bn254/tests/bls_signature.rs b/halo2-ecc/src/bn254/tests/bls_signature.rs index 2bcf600..004179e 100644 --- a/halo2-ecc/src/bn254/tests/bls_signature.rs +++ b/halo2-ecc/src/bn254/tests/bls_signature.rs @@ -78,8 +78,8 @@ fn test_bls_signature() { .unwrap(); println!("num_advice: {num_advice}", num_advice = params.num_advice); - let msg_hash = G2Affine::from(G2Affine::generator() * Fr::from(123456)); - // let msg_hash = G2Affine::random(OsRng); + // let msg_hash = G2Affine::from(G2Affine::generator() * Fr::from(123456)); + let msg_hash = G2Affine::random(OsRng); // println!("hash(m):{:?}",msg_hash); let g1 = G1Affine::generator(); // println!("g1:{:?}",g1); @@ -161,9 +161,9 @@ fn bench_bls_signature() -> Result<(), Box> { bench_params.limb_bits, bench_params.num_limbs, bench_params.num_aggregation, - stats.proof_time.time.elapsed(), + stats.proof_time, stats.proof_size, - stats.verify_time.time.elapsed() + stats.verify_time, )?; } Ok(()) diff --git a/halo2-ecc/src/bn254/tests/mod.rs b/halo2-ecc/src/bn254/tests/mod.rs index 4ecc7e9..780e261 100644 --- a/halo2-ecc/src/bn254/tests/mod.rs +++ b/halo2-ecc/src/bn254/tests/mod.rs @@ -27,6 +27,7 @@ pub mod msm; pub mod msm_sum_infinity; pub mod msm_sum_infinity_fixed_base; pub mod pairing; +pub mod msp; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] diff --git a/halo2-ecc/src/bn254/tests/msp.rs b/halo2-ecc/src/bn254/tests/msp.rs new file mode 100644 index 0000000..1a3f949 --- /dev/null +++ b/halo2-ecc/src/bn254/tests/msp.rs @@ -0,0 +1,168 @@ +use std::{ + fs::{self, File}, + io::{BufRead, BufReader}, +}; +// use env_logger::init; +use halo2_base::{gates::GateChip, halo2_proofs::{arithmetic::CurveAffine, halo2curves::serde::SerdeObject}, poseidon::hasher::{spec::OptimizedPoseidonSpec, PoseidonHasher}, utils::ScalarField}; +use halo2_base::Context; +use halo2_base::utils::BigPrimeField; +use itertools::Itertools; +// use rand_core::OsRng; +// use rand::rngs::OsRng; +use serde::{Serialize, Deserialize}; +use super::*; +use crate::bn254::{ + MerkleInfo, merkle_tree::MerkleTreeChip, + bls_signature::BlsSignatureChip, + msp::MspChip, +}; +use crate::halo2_proofs::halo2curves::bn256::G2Affine; +use std::io::Read; +use rand::seq::SliceRandom; // For random selection +use poseidon::Poseidon; + + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct key_struct { + sk: String, + pk_x: String, + pk_y: String, +} + + + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct MspData { + degree: u32, + message: String, + hash_msg: String, + keys: Vec, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +struct CombineBlsMtCircuitParams { + strategy: FpStrategy, + degree: u32, + num_advice: usize, + num_lookup_advice: usize, + num_fixed: usize, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, + num_aggregation: u32, + num_origin:u32, +} +fn f_from_string(s: &str) -> F { + let bytes:[u8; 32] = hex::decode(s).expect("Invalid hex string").try_into().expect("Invalid Fr bytes"); + F::from_bytes_le(&bytes) +} +fn fr_from_string(s: &str) -> Fr { + let bytes:[u8; 32] = hex::decode(s).expect("Invalid hex string").try_into().expect("Invalid Fr bytes"); + let result = Fr::from_bytes(&bytes).unwrap(); + result +} +fn fq_from_string(s: &str) -> Fq { + let bytes:[u8; 32] = hex::decode(s).expect("Invalid hex string").try_into().expect("Invalid Fr bytes"); + let result = Fq::from_bytes(&bytes).unwrap(); + result +} + +fn msp_test( + ctx: &mut Context, + range: &RangeChip, + params: CombineBlsMtCircuitParams, + g1: G1Affine, + signatures: &[G2Affine], + pubkeys: &[G1Affine], + msghash: G2Affine, + weighting_seed: F, + ivk: G1Affine, + isig: G2Affine, +) { + let fp_chip = FpChip::::new(range, params.limb_bits, params.num_limbs); + let pairing_chip = PairingChip::new(&fp_chip); + let bls_signature_chip = BlsSignatureChip::new(&fp_chip, &pairing_chip); + let gate_chip = GateChip::::default(); + let mut poseidon_chip = PoseidonHasher::::new(OptimizedPoseidonSpec::new::<8, 57, 0>()); + poseidon_chip.initialize_consts(ctx, &gate_chip); + let msp_chip = MspChip::new(&bls_signature_chip, &poseidon_chip); + let result = msp_chip.msp_verify(ctx, g1, signatures, pubkeys, msghash,weighting_seed,ivk,isig); + + assert_eq!(*result.value(), F::ONE); +} +#[test] +fn test_msp() { + let run_path = "configs/bn254/msp.config"; + let path = run_path; + let params: CombineBlsMtCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ).unwrap(); + + let merkle_input_path = "data/data_for_msp_{num}.json".replace("{num}", ¶ms.num_origin.to_string()); + let mut file = File::open(merkle_input_path).expect("Unable to open file"); + let mut data = String::new(); + file.read_to_string(&mut data).expect("Unable to read file"); + + let json_data: MspData = serde_json::from_str(&data).expect("Invalid JSON"); + let message = json_data.message.clone(); + let message = f_from_string::(&message); + let msg_hash = json_data.hash_msg.clone(); + let msg_hash_to_fr = fr_from_string(&msg_hash); + let msg_hash = G2Affine::from(G2Affine::generator() * msg_hash_to_fr); + + let mut rng = rand::thread_rng(); + let num_agg = params.num_aggregation as usize; + let selected_keys = json_data.keys.choose_multiple(&mut rng, num_agg).collect_vec(); + let sks: Vec = selected_keys.iter().map(|x| fr_from_string(&x.sk)).collect_vec(); + let pubkeys: Vec = selected_keys.iter().map(|x| G1Affine::from_xy(fq_from_string(&x.pk_x), fq_from_string(&x.pk_y)).unwrap()).collect_vec(); + + let signatures = sks.iter().map(|x| G2Affine::from(msg_hash * x)).collect_vec(); + + // weighting_seed = H(signatures[0].x,signatures[1].x,...) + let mut hasher = Poseidon::::new(8, 57); + let mut sigs_x: Vec = Vec::new(); + for sig in signatures.iter() { + let sig_x_c0_bytes = sig.x.c0.to_bytes(); + let six_x_c0_fr = Fr::from_bytes(&sig_x_c0_bytes).unwrap(); + sigs_x.push(six_x_c0_fr); + } + hasher.update(&sigs_x[..]); + let weighting_seed = hasher.squeeze(); + + // e_i = H(i,weighting_seed) for i in 0..n where n is the number of public keys + let e_is = pubkeys.iter().enumerate().map(|(i, _)| { + let mut hasher = Poseidon::::new(8, 57); + hasher.update(&[Fr::from(i as u64), weighting_seed]); + hasher.squeeze() + }).collect::>(); + + + + // ivk = \sigma pk_i*e_i + let products = pubkeys.iter().zip(e_is.iter()).map(|(pk, e_i)| { + G1Affine::from(pk * (*e_i)) + }).collect::>(); + + let mut ivk = products[0]; + for product in products.iter().skip(1) { + ivk = (ivk + product).into(); + } + + // isig = \sigma sig_i*e_i + let products = signatures.iter().zip(e_is.iter()).map(|(sig, e_i)| { + G2Affine::from(sig * (*e_i)) + }).collect::>(); + + let mut isig = products[0]; + for product in products.iter().skip(1) { + isig = (isig + product).into(); + } + + + + + base_test().k(params.degree).lookup_bits(params.lookup_bits).run(|ctx, range| { + msp_test(ctx,range,params, G1Affine::generator(), &signatures, &pubkeys, msg_hash, weighting_seed, ivk, isig); + }); +} + diff --git a/halo2-ecc/src/lib.rs b/halo2-ecc/src/lib.rs index 5b3f191..d4ae77b 100644 --- a/halo2-ecc/src/lib.rs +++ b/halo2-ecc/src/lib.rs @@ -13,6 +13,6 @@ pub mod secp256k1; pub use halo2_base; pub(crate) use halo2_base::halo2_proofs; -use halo2_proofs::halo2curves; -use halo2curves::ff; -use halo2curves::group; +pub use halo2_proofs::halo2curves; +pub use halo2curves::ff; +pub use halo2curves::group; diff --git a/hashes/zkevm/Cargo.toml b/hashes/zkevm/Cargo.toml index 2294526..5dee385 100644 --- a/hashes/zkevm/Cargo.toml +++ b/hashes/zkevm/Cargo.toml @@ -39,7 +39,8 @@ test-case = "3.1.0" sha2 = "0.10.7" [features] -default = ["halo2-axiom", "display"] +# default = ["halo2-axiom", "display"] +default = ["halo2-pse", "display"] display = ["snark-verifier-sdk/display"] halo2-pse = ["halo2-base/halo2-pse", "snark-verifier-sdk/halo2-pse"] halo2-axiom = ["halo2-base/halo2-axiom", "snark-verifier-sdk/halo2-axiom"] diff --git a/rust-toolchain b/rust-toolchain index 36e57ce..a15dab8 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-02-08 +nightly-2024-08-08