diff --git a/Cargo.toml b/Cargo.toml index 31bbef5c..122ba14b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,3 +3,4 @@ members = [ "snark-verifier", "snark-verifier-sdk" ] +resolver = "2" diff --git a/snark-verifier-sdk/Cargo.toml b/snark-verifier-sdk/Cargo.toml index 2f1ba1a3..60a35f61 100644 --- a/snark-verifier-sdk/Cargo.toml +++ b/snark-verifier-sdk/Cargo.toml @@ -20,11 +20,11 @@ ark-std = { version = "0.3.0", features = ["print-trace"], optional = true } snark-verifier = { path = "../snark-verifier", default-features = false } # system_halo2 -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2", tag = "v2023_04_20" } # not optional for now -halo2curves = { git = 'https://github.com/privacy-scaling-explorations/halo2curves', tag = "0.3.2" } # must be same version as in halo2_proofs +halo2_proofs = { git = "https://github.com/zkonduit/halo2" } +halo2curves = { package ="halo2curves", version = "0.6.0", features = ["derive_serde"] } # loader_halo2 -halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc", optional = true } +halo2_wrong_ecc = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "ecc", optional = true } poseidon = { git = "https://github.com/privacy-scaling-explorations/poseidon", tag = "v2023_04_20", optional = true } # loader_evm diff --git a/snark-verifier-sdk/examples/standard_plonk.rs b/snark-verifier-sdk/examples/standard_plonk.rs deleted file mode 100644 index 03c36f02..00000000 --- a/snark-verifier-sdk/examples/standard_plonk.rs +++ /dev/null @@ -1,184 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use halo2_proofs::halo2curves as halo2_curves; -use halo2_proofs::plonk::Circuit; -use halo2_proofs::{halo2curves::bn256::Bn256, poly::kzg::commitment::ParamsKZG}; -use rand::rngs::OsRng; -use snark_verifier_sdk::evm::{evm_verify, gen_evm_proof_shplonk, gen_evm_verifier_shplonk}; -use snark_verifier_sdk::halo2::gen_srs; -use snark_verifier_sdk::{ - gen_pk, - halo2::{aggregation::AggregationCircuit, gen_snark_shplonk}, - Snark, -}; -use snark_verifier_sdk::{CircuitExt, SHPLONK}; -use std::path::Path; - -mod application { - use super::halo2_curves::bn256::Fr; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance}, - poly::Rotation, - }; - use rand::RngCore; - use snark_verifier_sdk::CircuitExt; - - #[derive(Clone, Copy)] - pub struct StandardPlonkConfig { - a: Column, - b: Column, - c: Column, - q_a: Column, - q_b: Column, - q_c: Column, - q_ab: Column, - constant: Column, - #[allow(dead_code)] - instance: Column, - } - - impl StandardPlonkConfig { - fn configure(meta: &mut ConstraintSystem) -> Self { - let [a, b, c] = [(); 3].map(|_| meta.advice_column()); - let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); - let instance = meta.instance_column(); - - [a, b, c].map(|column| meta.enable_equality(column)); - - meta.create_gate( - "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", - |meta| { - let [a, b, c] = - [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); - let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] - .map(|column| meta.query_fixed(column, Rotation::cur())); - let instance = meta.query_instance(instance, Rotation::cur()); - Some( - q_a * a.clone() - + q_b * b.clone() - + q_c * c - + q_ab * a * b - + constant - + instance, - ) - }, - ); - - StandardPlonkConfig { - a, - b, - c, - q_a, - q_b, - q_c, - q_ab, - constant, - instance, - } - } - } - - #[derive(Clone, Default)] - pub struct StandardPlonk(Fr); - - impl StandardPlonk { - pub fn rand(mut rng: R) -> Self { - Self(Fr::from(rng.next_u32() as u64)) - } - } - - impl CircuitExt for StandardPlonk { - fn num_instance(&self) -> Vec { - vec![1] - } - - fn instances(&self) -> Vec> { - vec![vec![self.0]] - } - } - - impl Circuit for StandardPlonk { - type Config = StandardPlonkConfig; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - meta.set_minimum_degree(4); - StandardPlonkConfig::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - layouter.assign_region( - || "", - |mut region| { - region.assign_advice(|| "", config.a, 0, || Value::known(self.0))?; - region.assign_fixed(|| "", config.q_a, 0, || Value::known(-Fr::one()))?; - region.assign_advice(|| "", config.a, 1, || Value::known(-Fr::from(5u64)))?; - for (idx, column) in (1..).zip([ - config.q_a, - config.q_b, - config.q_c, - config.q_ab, - config.constant, - ]) { - region.assign_fixed( - || "", - column, - 1, - || Value::known(Fr::from(idx as u64)), - )?; - } - let a = region.assign_advice(|| "", config.a, 2, || Value::known(Fr::one()))?; - a.copy_advice(|| "", &mut region, config.b, 3)?; - a.copy_advice(|| "", &mut region, config.c, 4)?; - - Ok(()) - }, - ) - } - } -} - -fn gen_application_snark(params: &ParamsKZG) -> Snark { - let circuit = application::StandardPlonk::rand(OsRng); - - let pk = gen_pk(params, &circuit, Some(Path::new("./examples/app.pk"))); - gen_snark_shplonk(params, &pk, circuit, None::<&str>) -} - -fn main() { - let params_app = gen_srs(8); - let snarks = [(); 3].map(|_| gen_application_snark(¶ms_app)); - - let params = gen_srs(22); - let agg_circuit = AggregationCircuit::::new(¶ms, snarks); - - let start0 = start_timer!(|| "gen vk & pk"); - let pk = gen_pk( - ¶ms, - &agg_circuit.without_witnesses(), - Some(Path::new("./examples/agg.pk")), - ); - end_timer!(start0); - - let num_instances = agg_circuit.num_instance(); - let instances = agg_circuit.instances(); - let proof_calldata = gen_evm_proof_shplonk(¶ms, &pk, agg_circuit, instances.clone()); - - let deployment_code = gen_evm_verifier_shplonk::>( - ¶ms, - pk.get_vk(), - num_instances, - Some(Path::new("./examples/StandardPlonkVerifierExample.sol")), - ); - evm_verify(deployment_code, instances, proof_calldata); -} diff --git a/snark-verifier-sdk/src/evm.rs b/snark-verifier-sdk/src/evm.rs deleted file mode 100644 index 8cb703ce..00000000 --- a/snark-verifier-sdk/src/evm.rs +++ /dev/null @@ -1,192 +0,0 @@ -use crate::{GWC, SHPLONK}; - -use super::{CircuitExt, PlonkVerifier}; -#[cfg(feature = "display")] -use ark_std::{end_timer, start_timer}; -use halo2_proofs::{ - halo2curves::bn256::{Bn256, Fq, Fr, G1Affine}, - plonk::{create_proof, verify_proof, Circuit, ProvingKey, VerifyingKey}, - poly::{ - commitment::{ParamsProver, Prover, Verifier}, - kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - msm::DualMSM, - multiopen::{ProverGWC, ProverSHPLONK, VerifierGWC, VerifierSHPLONK}, - strategy::{AccumulatorStrategy, GuardKZG}, - }, - VerificationStrategy, - }, - transcript::{TranscriptReadBuffer, TranscriptWriterBuffer}, -}; -use itertools::Itertools; -use rand::{rngs::StdRng, SeedableRng}; -pub use snark_verifier::loader::evm::encode_calldata; -use snark_verifier::{ - loader::evm::{compile_solidity, deploy_and_call, EvmLoader}, - pcs::{ - kzg::{KzgAccumulator, KzgAsVerifyingKey, KzgDecidingKey, KzgSuccinctVerifyingKey}, - AccumulationDecider, AccumulationScheme, PolynomialCommitmentScheme, - }, - system::halo2::{compile, transcript::evm::EvmTranscript, Config}, - verifier::SnarkVerifier, -}; -use std::{fs, io, path::Path, rc::Rc}; - -/// Generates a proof for evm verification using either SHPLONK or GWC proving method. Uses Keccak for Fiat-Shamir. -pub fn gen_evm_proof<'params, C, P, V>( - params: &'params ParamsKZG, - pk: &'params ProvingKey, - circuit: C, - instances: Vec>, -) -> Vec -where - C: Circuit, - P: Prover<'params, KZGCommitmentScheme>, - V: Verifier< - 'params, - KZGCommitmentScheme, - Guard = GuardKZG<'params, Bn256>, - MSMAccumulator = DualMSM<'params, Bn256>, - >, -{ - let instances = instances - .iter() - .map(|instances| instances.as_slice()) - .collect_vec(); - - #[cfg(feature = "display")] - let proof_time = start_timer!(|| "Create EVM proof"); - let rng = StdRng::from_entropy(); - let proof = { - let mut transcript = TranscriptWriterBuffer::<_, G1Affine, _>::init(Vec::new()); - create_proof::, P, _, _, EvmTranscript<_, _, _, _>, _>( - params, - pk, - &[circuit], - &[instances.as_slice()], - rng, - &mut transcript, - ) - .unwrap(); - transcript.finalize() - }; - #[cfg(feature = "display")] - end_timer!(proof_time); - - let accept = { - let mut transcript = TranscriptReadBuffer::<_, G1Affine, _>::init(proof.as_slice()); - VerificationStrategy::<_, V>::finalize( - verify_proof::<_, V, _, EvmTranscript<_, _, _, _>, _>( - params.verifier_params(), - pk.get_vk(), - AccumulatorStrategy::new(params.verifier_params()), - &[instances.as_slice()], - &mut transcript, - ) - .unwrap(), - ) - }; - assert!(accept); - - proof -} - -pub fn gen_evm_proof_gwc<'params, C: Circuit>( - params: &'params ParamsKZG, - pk: &'params ProvingKey, - circuit: C, - instances: Vec>, -) -> Vec { - gen_evm_proof::, VerifierGWC<_>>(params, pk, circuit, instances) -} - -pub fn gen_evm_proof_shplonk<'params, C: Circuit>( - params: &'params ParamsKZG, - pk: &'params ProvingKey, - circuit: C, - instances: Vec>, -) -> Vec { - gen_evm_proof::, VerifierSHPLONK<_>>(params, pk, circuit, instances) -} - -pub fn gen_evm_verifier( - params: &ParamsKZG, - vk: &VerifyingKey, - num_instance: Vec, - path: Option<&Path>, -) -> Vec -where - C: CircuitExt, - AS: PolynomialCommitmentScheme< - G1Affine, - Rc, - VerifyingKey = KzgSuccinctVerifyingKey, - Output = KzgAccumulator>, - > + AccumulationScheme< - G1Affine, - Rc, - VerifyingKey = KzgAsVerifyingKey, - Accumulator = KzgAccumulator>, - > + AccumulationDecider, DecidingKey = KzgDecidingKey>, -{ - let protocol = compile( - params, - vk, - Config::kzg() - .with_num_instance(num_instance.clone()) - .with_accumulator_indices(C::accumulator_indices()), - ); - // deciding key - let dk = (params.get_g()[0], params.g2(), params.s_g2()).into(); - - let loader = EvmLoader::new::(); - let protocol = protocol.loaded(&loader); - let mut transcript = EvmTranscript::<_, Rc, _, _>::new(&loader); - - let instances = transcript.load_instances(num_instance); - let proof = - PlonkVerifier::::read_proof(&dk, &protocol, &instances, &mut transcript).unwrap(); - PlonkVerifier::::verify(&dk, &protocol, &instances, &proof).unwrap(); - - let sol_code = loader.solidity_code(); - let byte_code = compile_solidity(&sol_code); - if let Some(path) = path { - path.parent() - .and_then(|dir| fs::create_dir_all(dir).ok()) - .unwrap(); - fs::write(path, sol_code).unwrap(); - } - byte_code -} - -pub fn gen_evm_verifier_gwc>( - params: &ParamsKZG, - vk: &VerifyingKey, - num_instance: Vec, - path: Option<&Path>, -) -> Vec { - gen_evm_verifier::(params, vk, num_instance, path) -} - -pub fn gen_evm_verifier_shplonk>( - params: &ParamsKZG, - vk: &VerifyingKey, - num_instance: Vec, - path: Option<&Path>, -) -> Vec { - gen_evm_verifier::(params, vk, num_instance, path) -} - -pub fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) -> u64 { - let calldata = encode_calldata(&instances, &proof); - let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); - dbg!(gas_cost); - gas_cost -} - -pub fn write_calldata(instances: &[Vec], proof: &[u8], path: &Path) -> io::Result { - let calldata = encode_calldata(instances, proof); - let calldata = hex::encode(calldata); - fs::write(path, &calldata)?; - Ok(calldata) -} diff --git a/snark-verifier-sdk/src/halo2.rs b/snark-verifier-sdk/src/halo2.rs index 7cb4d6ff..823a5c5b 100644 --- a/snark-verifier-sdk/src/halo2.rs +++ b/snark-verifier-sdk/src/halo2.rs @@ -171,6 +171,7 @@ where AccumulatorStrategy::new(params.verifier_params()), &[instances.as_slice()], &mut transcript_read, + 0, ) .unwrap(), ) diff --git a/snark-verifier-sdk/src/lib.rs b/snark-verifier-sdk/src/lib.rs index 758a413b..17d59a47 100644 --- a/snark-verifier-sdk/src/lib.rs +++ b/snark-verifier-sdk/src/lib.rs @@ -25,8 +25,6 @@ use std::{ path::Path, }; -#[cfg(feature = "loader_evm")] -pub mod evm; #[cfg(feature = "loader_halo2")] pub mod halo2; diff --git a/snark-verifier/Cargo.toml b/snark-verifier/Cargo.toml index 962a57e0..3649d056 100644 --- a/snark-verifier/Cargo.toml +++ b/snark-verifier/Cargo.toml @@ -11,20 +11,21 @@ num-integer = "0.1.45" num-traits = "0.2.15" rand = "0.8" hex = "0.4" -halo2_curves = { git = "https://github.com/privacy-scaling-explorations/halo2curves", tag = "0.3.2", package = "halo2curves" } +halo2_curves = { package ="halo2curves", version = "0.6.0", features = ["derive_serde"] } + # parallel rayon = { version = "1.5.3", optional = true } # system_halo2 -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2", tag = "v2023_04_20", optional = true } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", optional = true } # loader_evm sha3 = { version = "0.10", optional = true } revm = { version = "3.5.0", optional = true, default-features = false } # loader_halo2 -halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc", optional = true } +halo2_wrong_ecc = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "ecc", optional = true } poseidon = { git = "https://github.com/privacy-scaling-explorations/poseidon", tag = "v2023_04_20", optional = true } # derive_serde @@ -35,13 +36,14 @@ rand_chacha = "0.3.1" paste = "1.0.7" # system_halo2 -halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc" } +halo2_wrong_ecc = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "ecc" } + [features] default = ["loader_evm", "loader_halo2", "system_halo2"] parallel = ["dep:rayon"] - +mv-lookup = ["halo2_proofs?/mv-lookup"] # loaders loader_evm = ["dep:sha3", "dep:revm"] loader_halo2 = ["dep:halo2_proofs", "dep:halo2_wrong_ecc", "dep:poseidon"] @@ -56,10 +58,4 @@ halo2_circuit_params = [ ] derive_serde = ["dep:serde"] -[[example]] -name = "evm-verifier" -required-features = ["loader_evm", "system_halo2"] -[[example]] -name = "evm-verifier-with-accumulator" -required-features = ["loader_halo2", "loader_evm", "system_halo2"] diff --git a/snark-verifier/examples/evm-verifier-with-accumulator.rs b/snark-verifier/examples/evm-verifier-with-accumulator.rs deleted file mode 100644 index a4415297..00000000 --- a/snark-verifier/examples/evm-verifier-with-accumulator.rs +++ /dev/null @@ -1,619 +0,0 @@ -use halo2_curves::bn256::{Bn256, Fq, Fr, G1Affine}; -use halo2_proofs::{ - dev::MockProver, - plonk::{create_proof, keygen_pk, keygen_vk, verify_proof, Circuit, ProvingKey, VerifyingKey}, - poly::{ - commitment::{Params, ParamsProver}, - kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - multiopen::{ProverGWC, VerifierGWC}, - strategy::AccumulatorStrategy, - }, - VerificationStrategy, - }, - transcript::{EncodedChallenge, TranscriptReadBuffer, TranscriptWriterBuffer}, -}; -use itertools::Itertools; -use rand::rngs::OsRng; -use snark_verifier::{ - loader::{ - evm::{self, deploy_and_call, encode_calldata, EvmLoader}, - native::NativeLoader, - }, - pcs::kzg::{Gwc19, KzgAs, LimbsEncoding}, - system::halo2::{compile, transcript::evm::EvmTranscript, Config}, - verifier::{self, SnarkVerifier}, -}; -use std::{io::Cursor, rc::Rc}; - -const LIMBS: usize = 4; -const BITS: usize = 68; - -type As = KzgAs; -type PlonkSuccinctVerifier = verifier::plonk::PlonkSuccinctVerifier>; -type PlonkVerifier = verifier::plonk::PlonkVerifier>; - -mod application { - use halo2_curves::{bn256::Fr, ff::Field}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance}, - poly::Rotation, - }; - use rand::RngCore; - - #[derive(Clone, Copy)] - pub struct StandardPlonkConfig { - a: Column, - b: Column, - c: Column, - q_a: Column, - q_b: Column, - q_c: Column, - q_ab: Column, - constant: Column, - #[allow(dead_code)] - instance: Column, - } - - impl StandardPlonkConfig { - fn configure(meta: &mut ConstraintSystem) -> Self { - let [a, b, c] = [(); 3].map(|_| meta.advice_column()); - let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); - let instance = meta.instance_column(); - - [a, b, c].map(|column| meta.enable_equality(column)); - - meta.create_gate( - "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", - |meta| { - let [a, b, c] = - [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); - let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] - .map(|column| meta.query_fixed(column, Rotation::cur())); - let instance = meta.query_instance(instance, Rotation::cur()); - Some( - q_a * a.clone() - + q_b * b.clone() - + q_c * c - + q_ab * a * b - + constant - + instance, - ) - }, - ); - - StandardPlonkConfig { - a, - b, - c, - q_a, - q_b, - q_c, - q_ab, - constant, - instance, - } - } - } - - #[derive(Clone, Default)] - pub struct StandardPlonk(Fr); - - impl StandardPlonk { - pub fn rand(mut rng: R) -> Self { - Self(Fr::from(rng.next_u32() as u64)) - } - - pub fn num_instance() -> Vec { - vec![1] - } - - pub fn instances(&self) -> Vec> { - vec![vec![self.0]] - } - } - - impl Circuit for StandardPlonk { - type Config = StandardPlonkConfig; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - meta.set_minimum_degree(4); - StandardPlonkConfig::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - layouter.assign_region( - || "", - |mut region| { - region.assign_advice(|| "", config.a, 0, || Value::known(self.0))?; - region.assign_fixed(|| "", config.q_a, 0, || Value::known(-Fr::ONE))?; - - region.assign_advice(|| "", config.a, 1, || Value::known(-Fr::from(5)))?; - for (idx, column) in (1..).zip([ - config.q_a, - config.q_b, - config.q_c, - config.q_ab, - config.constant, - ]) { - region.assign_fixed(|| "", column, 1, || Value::known(Fr::from(idx)))?; - } - - let a = region.assign_advice(|| "", config.a, 2, || Value::known(Fr::ONE))?; - a.copy_advice(|| "", &mut region, config.b, 3)?; - a.copy_advice(|| "", &mut region, config.c, 4)?; - - Ok(()) - }, - ) - } - } -} - -mod aggregation { - use super::{As, PlonkSuccinctVerifier, BITS, LIMBS}; - use halo2_curves::bn256::{Bn256, Fq, Fr, G1Affine}; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{self, Circuit, ConstraintSystem, Error}, - poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, - }; - use halo2_wrong_ecc::{ - integer::rns::Rns, - maingate::{ - MainGate, MainGateConfig, MainGateInstructions, RangeChip, RangeConfig, - RangeInstructions, RegionCtx, - }, - EccConfig, - }; - use itertools::Itertools; - use rand::rngs::OsRng; - use snark_verifier::{ - loader::{self, native::NativeLoader}, - pcs::{ - kzg::{KzgAccumulator, KzgSuccinctVerifyingKey, LimbsEncodingInstructions}, - AccumulationScheme, AccumulationSchemeProver, - }, - system, - util::arithmetic::{fe_to_limbs, PrimeField}, - verifier::{plonk::PlonkProtocol, SnarkVerifier}, - }; - use std::rc::Rc; - - const T: usize = 5; - const RATE: usize = 4; - const R_F: usize = 8; - const R_P: usize = 60; - - type Svk = KzgSuccinctVerifyingKey; - type BaseFieldEccChip = halo2_wrong_ecc::BaseFieldEccChip; - type Halo2Loader<'a> = loader::halo2::Halo2Loader<'a, G1Affine, BaseFieldEccChip>; - pub type PoseidonTranscript = - system::halo2::transcript::halo2::PoseidonTranscript; - - pub struct Snark { - protocol: PlonkProtocol, - instances: Vec>, - proof: Vec, - } - - impl Snark { - pub fn new( - protocol: PlonkProtocol, - instances: Vec>, - proof: Vec, - ) -> Self { - Self { - protocol, - instances, - proof, - } - } - } - - impl From for SnarkWitness { - fn from(snark: Snark) -> Self { - Self { - protocol: snark.protocol, - instances: snark - .instances - .into_iter() - .map(|instances| instances.into_iter().map(Value::known).collect_vec()) - .collect(), - proof: Value::known(snark.proof), - } - } - } - - #[derive(Clone)] - pub struct SnarkWitness { - protocol: PlonkProtocol, - instances: Vec>>, - proof: Value>, - } - - impl SnarkWitness { - fn without_witnesses(&self) -> Self { - SnarkWitness { - protocol: self.protocol.clone(), - instances: self - .instances - .iter() - .map(|instances| vec![Value::unknown(); instances.len()]) - .collect(), - proof: Value::unknown(), - } - } - - fn proof(&self) -> Value<&[u8]> { - self.proof.as_ref().map(Vec::as_slice) - } - } - - pub fn aggregate<'a>( - svk: &Svk, - loader: &Rc>, - snarks: &[SnarkWitness], - as_proof: Value<&'_ [u8]>, - ) -> KzgAccumulator>> { - let assign_instances = |instances: &[Vec>]| { - instances - .iter() - .map(|instances| { - instances - .iter() - .map(|instance| loader.assign_scalar(*instance)) - .collect_vec() - }) - .collect_vec() - }; - - let accumulators = snarks - .iter() - .flat_map(|snark| { - let protocol = snark.protocol.loaded(loader); - let instances = assign_instances(&snark.instances); - let mut transcript = - PoseidonTranscript::, _>::new(loader, snark.proof()); - let proof = - PlonkSuccinctVerifier::read_proof(svk, &protocol, &instances, &mut transcript) - .unwrap(); - PlonkSuccinctVerifier::verify(svk, &protocol, &instances, &proof).unwrap() - }) - .collect_vec(); - - let acccumulator = { - let mut transcript = PoseidonTranscript::, _>::new(loader, as_proof); - let proof = - As::read_proof(&Default::default(), &accumulators, &mut transcript).unwrap(); - As::verify(&Default::default(), &accumulators, &proof).unwrap() - }; - - acccumulator - } - - #[derive(Clone)] - pub struct AggregationConfig { - main_gate_config: MainGateConfig, - range_config: RangeConfig, - } - - impl AggregationConfig { - pub fn configure( - meta: &mut ConstraintSystem, - composition_bits: Vec, - overflow_bits: Vec, - ) -> Self { - let main_gate_config = MainGate::::configure(meta); - let range_config = - RangeChip::::configure(meta, &main_gate_config, composition_bits, overflow_bits); - AggregationConfig { - main_gate_config, - range_config, - } - } - - pub fn main_gate(&self) -> MainGate { - MainGate::new(self.main_gate_config.clone()) - } - - pub fn range_chip(&self) -> RangeChip { - RangeChip::new(self.range_config.clone()) - } - - pub fn ecc_chip(&self) -> BaseFieldEccChip { - BaseFieldEccChip::new(EccConfig::new( - self.range_config.clone(), - self.main_gate_config.clone(), - )) - } - } - - #[derive(Clone)] - pub struct AggregationCircuit { - svk: Svk, - snarks: Vec, - instances: Vec, - as_proof: Value>, - } - - impl AggregationCircuit { - pub fn new(params: &ParamsKZG, snarks: impl IntoIterator) -> Self { - let svk = params.get_g()[0].into(); - let snarks = snarks.into_iter().collect_vec(); - - let accumulators = snarks - .iter() - .flat_map(|snark| { - let mut transcript = - PoseidonTranscript::::new(snark.proof.as_slice()); - let proof = PlonkSuccinctVerifier::read_proof( - &svk, - &snark.protocol, - &snark.instances, - &mut transcript, - ) - .unwrap(); - PlonkSuccinctVerifier::verify(&svk, &snark.protocol, &snark.instances, &proof) - .unwrap() - }) - .collect_vec(); - - let (accumulator, as_proof) = { - let mut transcript = PoseidonTranscript::::new(Vec::new()); - let accumulator = - As::create_proof(&Default::default(), &accumulators, &mut transcript, OsRng) - .unwrap(); - (accumulator, transcript.finalize()) - }; - - let KzgAccumulator { lhs, rhs } = accumulator; - let instances = [lhs.x, lhs.y, rhs.x, rhs.y] - .map(fe_to_limbs::<_, _, LIMBS, BITS>) - .concat(); - - Self { - svk, - snarks: snarks.into_iter().map_into().collect(), - instances, - as_proof: Value::known(as_proof), - } - } - - pub fn accumulator_indices() -> Vec<(usize, usize)> { - (0..4 * LIMBS).map(|idx| (0, idx)).collect() - } - - pub fn num_instance() -> Vec { - vec![4 * LIMBS] - } - - pub fn instances(&self) -> Vec> { - vec![self.instances.clone()] - } - - pub fn as_proof(&self) -> Value<&[u8]> { - self.as_proof.as_ref().map(Vec::as_slice) - } - } - - impl Circuit for AggregationCircuit { - type Config = AggregationConfig; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - Self { - svk: self.svk, - snarks: self - .snarks - .iter() - .map(SnarkWitness::without_witnesses) - .collect(), - instances: Vec::new(), - as_proof: Value::unknown(), - } - } - - fn configure(meta: &mut plonk::ConstraintSystem) -> Self::Config { - AggregationConfig::configure( - meta, - vec![BITS / LIMBS], - Rns::::construct().overflow_lengths(), - ) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), plonk::Error> { - let main_gate = config.main_gate(); - let range_chip = config.range_chip(); - - range_chip.load_table(&mut layouter)?; - - let accumulator_limbs = layouter.assign_region( - || "", - |region| { - let ctx = RegionCtx::new(region, 0); - - let ecc_chip = config.ecc_chip(); - let loader = Halo2Loader::new(ecc_chip, ctx); - let accumulator = aggregate(&self.svk, &loader, &self.snarks, self.as_proof()); - - let accumulator_limbs = [accumulator.lhs, accumulator.rhs] - .iter() - .map(|ec_point| { - loader.ecc_chip().assign_ec_point_to_limbs( - &mut loader.ctx_mut(), - ec_point.assigned(), - ) - }) - .collect::, Error>>()? - .into_iter() - .flatten(); - - Ok(accumulator_limbs) - }, - )?; - - for (row, limb) in accumulator_limbs.enumerate() { - main_gate.expose_public(layouter.namespace(|| ""), limb, row)?; - } - - Ok(()) - } - } -} - -fn gen_srs(k: u32) -> ParamsKZG { - ParamsKZG::::setup(k, OsRng) -} - -fn gen_pk>(params: &ParamsKZG, circuit: &C) -> ProvingKey { - let vk = keygen_vk(params, circuit).unwrap(); - keygen_pk(params, vk, circuit).unwrap() -} - -fn gen_proof< - C: Circuit, - E: EncodedChallenge, - TR: TranscriptReadBuffer>, G1Affine, E>, - TW: TranscriptWriterBuffer, G1Affine, E>, ->( - params: &ParamsKZG, - pk: &ProvingKey, - circuit: C, - instances: Vec>, -) -> Vec { - MockProver::run(params.k(), &circuit, instances.clone()) - .unwrap() - .assert_satisfied(); - - let instances = instances - .iter() - .map(|instances| instances.as_slice()) - .collect_vec(); - let proof = { - let mut transcript = TW::init(Vec::new()); - create_proof::, ProverGWC<_>, _, _, TW, _>( - params, - pk, - &[circuit], - &[instances.as_slice()], - OsRng, - &mut transcript, - ) - .unwrap(); - transcript.finalize() - }; - - let accept = { - let mut transcript = TR::init(Cursor::new(proof.clone())); - VerificationStrategy::<_, VerifierGWC<_>>::finalize( - verify_proof::<_, VerifierGWC<_>, _, TR, _>( - params.verifier_params(), - pk.get_vk(), - AccumulatorStrategy::new(params.verifier_params()), - &[instances.as_slice()], - &mut transcript, - ) - .unwrap(), - ) - }; - assert!(accept); - - proof -} - -fn gen_application_snark(params: &ParamsKZG) -> aggregation::Snark { - let circuit = application::StandardPlonk::rand(OsRng); - - let pk = gen_pk(params, &circuit); - let protocol = compile( - params, - pk.get_vk(), - Config::kzg().with_num_instance(application::StandardPlonk::num_instance()), - ); - - let proof = gen_proof::< - _, - _, - aggregation::PoseidonTranscript, - aggregation::PoseidonTranscript, - >(params, &pk, circuit.clone(), circuit.instances()); - aggregation::Snark::new(protocol, circuit.instances(), proof) -} - -fn gen_aggregation_evm_verifier( - params: &ParamsKZG, - vk: &VerifyingKey, - num_instance: Vec, - accumulator_indices: Vec<(usize, usize)>, -) -> Vec { - let protocol = compile( - params, - vk, - Config::kzg() - .with_num_instance(num_instance.clone()) - .with_accumulator_indices(Some(accumulator_indices)), - ); - let vk = (params.get_g()[0], params.g2(), params.s_g2()).into(); - - let loader = EvmLoader::new::(); - let protocol = protocol.loaded(&loader); - let mut transcript = EvmTranscript::<_, Rc, _, _>::new(&loader); - - let instances = transcript.load_instances(num_instance); - let proof = PlonkVerifier::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); - PlonkVerifier::verify(&vk, &protocol, &instances, &proof).unwrap(); - - evm::compile_solidity(&loader.solidity_code()) -} - -fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { - let calldata = encode_calldata(&instances, &proof); - let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); - dbg!(gas_cost); -} - -fn main() { - let params = gen_srs(22); - let params_app = { - let mut params = params.clone(); - params.downsize(8); - params - }; - - let snarks = [(); 3].map(|_| gen_application_snark(¶ms_app)); - let agg_circuit = aggregation::AggregationCircuit::new(¶ms, snarks); - let pk = gen_pk(¶ms, &agg_circuit); - let deployment_code = gen_aggregation_evm_verifier( - ¶ms, - pk.get_vk(), - aggregation::AggregationCircuit::num_instance(), - aggregation::AggregationCircuit::accumulator_indices(), - ); - - let proof = gen_proof::<_, _, EvmTranscript, EvmTranscript>( - ¶ms, - &pk, - agg_circuit.clone(), - agg_circuit.instances(), - ); - evm_verify(deployment_code, agg_circuit.instances(), proof); -} diff --git a/snark-verifier/examples/evm-verifier.rs b/snark-verifier/examples/evm-verifier.rs deleted file mode 100644 index cc011560..00000000 --- a/snark-verifier/examples/evm-verifier.rs +++ /dev/null @@ -1,246 +0,0 @@ -use halo2_curves::{ - bn256::{Bn256, Fq, Fr, G1Affine}, - ff::Field, -}; -use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, - dev::MockProver, - plonk::{ - create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, - ConstraintSystem, Error, Fixed, Instance, ProvingKey, VerifyingKey, - }, - poly::{ - commitment::{Params, ParamsProver}, - kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - multiopen::{ProverGWC, VerifierGWC}, - strategy::AccumulatorStrategy, - }, - Rotation, VerificationStrategy, - }, - transcript::{TranscriptReadBuffer, TranscriptWriterBuffer}, -}; -use itertools::Itertools; -use rand::{rngs::OsRng, RngCore}; -use snark_verifier::{ - loader::evm::{self, deploy_and_call, encode_calldata, EvmLoader}, - pcs::kzg::{Gwc19, KzgAs}, - system::halo2::{compile, transcript::evm::EvmTranscript, Config}, - verifier::{self, SnarkVerifier}, -}; -use std::rc::Rc; - -type PlonkVerifier = verifier::plonk::PlonkVerifier>; - -#[derive(Clone, Copy)] -struct StandardPlonkConfig { - a: Column, - b: Column, - c: Column, - q_a: Column, - q_b: Column, - q_c: Column, - q_ab: Column, - constant: Column, - #[allow(dead_code)] - instance: Column, -} - -impl StandardPlonkConfig { - fn configure(meta: &mut ConstraintSystem) -> Self { - let [a, b, c] = [(); 3].map(|_| meta.advice_column()); - let [q_a, q_b, q_c, q_ab, constant] = [(); 5].map(|_| meta.fixed_column()); - let instance = meta.instance_column(); - - [a, b, c].map(|column| meta.enable_equality(column)); - - meta.create_gate( - "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", - |meta| { - let [a, b, c] = [a, b, c].map(|column| meta.query_advice(column, Rotation::cur())); - let [q_a, q_b, q_c, q_ab, constant] = [q_a, q_b, q_c, q_ab, constant] - .map(|column| meta.query_fixed(column, Rotation::cur())); - let instance = meta.query_instance(instance, Rotation::cur()); - Some( - q_a * a.clone() - + q_b * b.clone() - + q_c * c - + q_ab * a * b - + constant - + instance, - ) - }, - ); - - StandardPlonkConfig { - a, - b, - c, - q_a, - q_b, - q_c, - q_ab, - constant, - instance, - } - } -} - -#[derive(Clone, Default)] -struct StandardPlonk(Fr); - -impl StandardPlonk { - fn rand(mut rng: R) -> Self { - Self(Fr::from(rng.next_u32() as u64)) - } - - fn num_instance() -> Vec { - vec![1] - } - - fn instances(&self) -> Vec> { - vec![vec![self.0]] - } -} - -impl Circuit for StandardPlonk { - type Config = StandardPlonkConfig; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - meta.set_minimum_degree(4); - StandardPlonkConfig::configure(meta) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - layouter.assign_region( - || "", - |mut region| { - region.assign_advice(|| "", config.a, 0, || Value::known(self.0))?; - region.assign_fixed(|| "", config.q_a, 0, || Value::known(-Fr::ONE))?; - - region.assign_advice(|| "", config.a, 1, || Value::known(-Fr::from(5)))?; - for (idx, column) in (1..).zip([ - config.q_a, - config.q_b, - config.q_c, - config.q_ab, - config.constant, - ]) { - region.assign_fixed(|| "", column, 1, || Value::known(Fr::from(idx)))?; - } - - let a = region.assign_advice(|| "", config.a, 2, || Value::known(Fr::ONE))?; - a.copy_advice(|| "", &mut region, config.b, 3)?; - a.copy_advice(|| "", &mut region, config.c, 4)?; - - Ok(()) - }, - ) - } -} - -fn gen_srs(k: u32) -> ParamsKZG { - ParamsKZG::::setup(k, OsRng) -} - -fn gen_pk>(params: &ParamsKZG, circuit: &C) -> ProvingKey { - let vk = keygen_vk(params, circuit).unwrap(); - keygen_pk(params, vk, circuit).unwrap() -} - -fn gen_proof>( - params: &ParamsKZG, - pk: &ProvingKey, - circuit: C, - instances: Vec>, -) -> Vec { - MockProver::run(params.k(), &circuit, instances.clone()) - .unwrap() - .assert_satisfied(); - - let instances = instances - .iter() - .map(|instances| instances.as_slice()) - .collect_vec(); - let proof = { - let mut transcript = TranscriptWriterBuffer::<_, G1Affine, _>::init(Vec::new()); - create_proof::, ProverGWC<_>, _, _, EvmTranscript<_, _, _, _>, _>( - params, - pk, - &[circuit], - &[instances.as_slice()], - OsRng, - &mut transcript, - ) - .unwrap(); - transcript.finalize() - }; - - let accept = { - let mut transcript = TranscriptReadBuffer::<_, G1Affine, _>::init(proof.as_slice()); - VerificationStrategy::<_, VerifierGWC<_>>::finalize( - verify_proof::<_, VerifierGWC<_>, _, EvmTranscript<_, _, _, _>, _>( - params.verifier_params(), - pk.get_vk(), - AccumulatorStrategy::new(params.verifier_params()), - &[instances.as_slice()], - &mut transcript, - ) - .unwrap(), - ) - }; - assert!(accept); - - proof -} - -fn gen_evm_verifier( - params: &ParamsKZG, - vk: &VerifyingKey, - num_instance: Vec, -) -> Vec { - let protocol = compile( - params, - vk, - Config::kzg().with_num_instance(num_instance.clone()), - ); - let vk = (params.get_g()[0], params.g2(), params.s_g2()).into(); - - let loader = EvmLoader::new::(); - let protocol = protocol.loaded(&loader); - let mut transcript = EvmTranscript::<_, Rc, _, _>::new(&loader); - - let instances = transcript.load_instances(num_instance); - let proof = PlonkVerifier::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); - PlonkVerifier::verify(&vk, &protocol, &instances, &proof).unwrap(); - - evm::compile_solidity(&loader.solidity_code()) -} - -fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { - let calldata = encode_calldata(&instances, &proof); - let gas_cost = deploy_and_call(deployment_code, calldata).unwrap(); - dbg!(gas_cost); -} - -fn main() { - let params = gen_srs(8); - - let circuit = StandardPlonk::rand(OsRng); - let pk = gen_pk(¶ms, &circuit); - let deployment_code = gen_evm_verifier(¶ms, pk.get_vk(), StandardPlonk::num_instance()); - - let proof = gen_proof(¶ms, &pk, circuit.clone(), circuit.instances()); - evm_verify(deployment_code, circuit.instances(), proof); -} diff --git a/snark-verifier/src/loader/evm/loader.rs b/snark-verifier/src/loader/evm/loader.rs index e3d82cf2..2c5ecffe 100644 --- a/snark-verifier/src/loader/evm/loader.rs +++ b/snark-verifier/src/loader/evm/loader.rs @@ -69,8 +69,8 @@ impl EvmLoader { /// Initialize a [`EvmLoader`] with base and scalar field. pub fn new() -> Rc where - Base: PrimeField, - Scalar: PrimeField, + Base: PrimeField>, + Scalar: PrimeField>, { let base_modulus = modulus::(); let scalar_modulus = modulus::(); @@ -500,7 +500,7 @@ impl PartialEq for EcPoint { impl LoadedEcPoint for EcPoint where C: CurveAffine, - C::ScalarExt: PrimeField, + C::ScalarExt: PrimeField>, { type Loader = Rc; @@ -654,7 +654,7 @@ impl PartialEq for Scalar { } } -impl> LoadedScalar for Scalar { +impl>> LoadedScalar for Scalar { type Loader = Rc; fn loader(&self) -> &Self::Loader { @@ -665,7 +665,7 @@ impl> LoadedScalar for Scalar { impl EcPointLoader for Rc where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { type LoadedEcPoint = EcPoint; @@ -695,7 +695,7 @@ where } } -impl> ScalarLoader for Rc { +impl>> ScalarLoader for Rc { type LoadedScalar = Scalar; fn load_const(&self, value: &F) -> Scalar { @@ -902,7 +902,7 @@ impl> ScalarLoader for Rc { impl Loader for Rc where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { #[cfg(test)] fn start_cost_metering(&self, identifier: &str) { diff --git a/snark-verifier/src/loader/evm/util.rs b/snark-verifier/src/loader/evm/util.rs index f07515cc..13e440f8 100644 --- a/snark-verifier/src/loader/evm/util.rs +++ b/snark-verifier/src/loader/evm/util.rs @@ -52,32 +52,32 @@ impl MemoryChunk { /// little-endian representation. pub fn fe_to_u256(f: F) -> U256 where - F: PrimeField, + F: PrimeField>, { - U256::from_le_bytes(f.to_repr()) + U256::from_le_bytes(f.to_repr().inner().clone()) } /// Convert a [`U256`] into a [`PrimeField`]. pub fn u256_to_fe(value: U256) -> F where - F: PrimeField, + F: PrimeField>, { let value = value % modulus::(); - F::from_repr(value.to_le_bytes::<32>()).unwrap() + F::from_repr(value.to_le_bytes::<32>().into()).unwrap() } /// Returns modulus of [`PrimeField`] as [`U256`]. pub fn modulus() -> U256 where - F: PrimeField, + F: PrimeField>, { - U256::from_le_bytes((-F::ONE).to_repr()) + U256::from(1) + U256::from_le_bytes((-F::ONE).to_repr().into()) + U256::from(1) } /// Encode instances and proof into calldata. pub fn encode_calldata(instances: &[Vec], proof: &[u8]) -> Vec where - F: PrimeField, + F: PrimeField>, { iter::empty() .chain( diff --git a/snark-verifier/src/pcs/kzg/accumulation.rs b/snark-verifier/src/pcs/kzg/accumulation.rs index 5139d49e..1f9bbb9f 100644 --- a/snark-verifier/src/pcs/kzg/accumulation.rs +++ b/snark-verifier/src/pcs/kzg/accumulation.rs @@ -19,7 +19,8 @@ pub struct KzgAs(PhantomData<(M, MOS)>); impl AccumulationScheme for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::G1Affine: CurveAffine, + M::Fr: PrimeField, L: Loader, MOS: Clone + Debug, { @@ -140,7 +141,8 @@ where impl AccumulationSchemeProver for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::G1Affine: CurveAffine, + M::Fr: PrimeField, MOS: Clone + Debug, { type ProvingKey = KzgAsProvingKey; @@ -165,7 +167,7 @@ where let blind = pk .zk() .then(|| { - let s = M::Scalar::random(rng); + let s = M::Fr::random(rng); let (g, s_g) = pk.0.unwrap(); let lhs = (s_g * s).to_affine(); let rhs = (g * s).to_affine(); diff --git a/snark-verifier/src/pcs/kzg/accumulator.rs b/snark-verifier/src/pcs/kzg/accumulator.rs index ed0d87a4..4910931f 100644 --- a/snark-verifier/src/pcs/kzg/accumulator.rs +++ b/snark-verifier/src/pcs/kzg/accumulator.rs @@ -102,7 +102,7 @@ mod evm { for LimbsEncoding where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { type Accumulator = KzgAccumulator>; diff --git a/snark-verifier/src/pcs/kzg/decider.rs b/snark-verifier/src/pcs/kzg/decider.rs index eb7e84a7..cdd00017 100644 --- a/snark-verifier/src/pcs/kzg/decider.rs +++ b/snark-verifier/src/pcs/kzg/decider.rs @@ -1,9 +1,14 @@ +use halo2_curves::CurveAffine; + use crate::{pcs::kzg::KzgSuccinctVerifyingKey, util::arithmetic::MultiMillerLoop}; use std::marker::PhantomData; /// KZG deciding key. #[derive(Debug, Clone, Copy)] -pub struct KzgDecidingKey { +pub struct KzgDecidingKey +where + M::G1Affine: CurveAffine, +{ /// KZG succinct verifying key. pub svk: KzgSuccinctVerifyingKey, /// Generator on G2. @@ -13,7 +18,10 @@ pub struct KzgDecidingKey { _marker: PhantomData, } -impl KzgDecidingKey { +impl KzgDecidingKey +where + M::G1Affine: CurveAffine, +{ /// Initialize a [`KzgDecidingKey`] pub fn new( svk: impl Into>, @@ -29,19 +37,27 @@ impl KzgDecidingKey { } } -impl From<(M::G1Affine, M::G2Affine, M::G2Affine)> for KzgDecidingKey { +impl From<(M::G1Affine, M::G2Affine, M::G2Affine)> for KzgDecidingKey +where + M::G1Affine: CurveAffine, +{ fn from((g1, g2, s_g2): (M::G1Affine, M::G2Affine, M::G2Affine)) -> KzgDecidingKey { KzgDecidingKey::new(g1, g2, s_g2) } } -impl AsRef> for KzgDecidingKey { +impl AsRef> for KzgDecidingKey +where + M::G1Affine: CurveAffine, +{ fn as_ref(&self) -> &KzgSuccinctVerifyingKey { &self.svk } } mod native { + use halo2_curves::CurveAffine; + use crate::{ loader::native::NativeLoader, pcs::{ @@ -59,7 +75,8 @@ mod native { impl AccumulationDecider for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::G1Affine: CurveAffine, + M::Fr: PrimeField, MOS: Clone + Debug, { type DecidingKey = KzgDecidingKey; @@ -113,7 +130,10 @@ mod evm { impl AccumulationDecider> for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::G1Affine: CurveAffine, ScalarExt = M::Fr>, + ::ScalarExt: PrimeField>, + M::G2Affine: CurveAffine, + M::Fr: PrimeField>, MOS: Clone + Debug, { type DecidingKey = KzgDecidingKey; @@ -162,7 +182,7 @@ mod evm { loader.code_mut().runtime_append(code); let challenge = loader.scalar(Value::Memory(challenge_ptr)); - let powers_of_challenge = LoadedScalar::::powers(&challenge, lhs.len()); + let powers_of_challenge = LoadedScalar::::powers(&challenge, lhs.len()); let [lhs, rhs] = [lhs, rhs].map(|msms| { msms.iter() .zip(powers_of_challenge.iter()) diff --git a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs index cbfa0000..f54ba253 100644 --- a/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs +++ b/snark-verifier/src/pcs/kzg/multiopen/bdfg21.rs @@ -27,7 +27,8 @@ pub struct Bdfg21; impl PolynomialCommitmentScheme for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField + Ord, + M::G1Affine: CurveAffine, + M::Fr: PrimeField + Ord, L: Loader, { type VerifyingKey = KzgSuccinctVerifyingKey; @@ -36,7 +37,7 @@ where fn read_proof( _: &KzgSuccinctVerifyingKey, - _: &[Query], + _: &[Query], transcript: &mut T, ) -> Result, Error> where @@ -49,7 +50,7 @@ where svk: &KzgSuccinctVerifyingKey, commitments: &[Msm], z: &L::LoadedScalar, - queries: &[Query], + queries: &[Query], proof: &Bdfg21Proof, ) -> Result { let sets = query_sets(queries); @@ -370,11 +371,11 @@ where impl CostEstimation for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::Fr: PrimeField, { - type Input = Vec>; + type Input = Vec>; - fn estimate_cost(_: &Vec>) -> Cost { + fn estimate_cost(_: &Vec>) -> Cost { Cost { num_commitment: 2, num_msm: 2, diff --git a/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs b/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs index b664d536..4e3af907 100644 --- a/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs +++ b/snark-verifier/src/pcs/kzg/multiopen/gwc19.rs @@ -23,7 +23,8 @@ pub struct Gwc19; impl PolynomialCommitmentScheme for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::G1Affine: CurveAffine, + M::Fr: PrimeField, L: Loader, { type VerifyingKey = KzgSuccinctVerifyingKey; @@ -32,7 +33,7 @@ where fn read_proof( _: &Self::VerifyingKey, - queries: &[Query], + queries: &[Query], transcript: &mut T, ) -> Result where @@ -45,7 +46,7 @@ where svk: &Self::VerifyingKey, commitments: &[Msm], z: &L::LoadedScalar, - queries: &[Query], + queries: &[Query], proof: &Self::Proof, ) -> Result { let sets = query_sets(queries); @@ -161,11 +162,11 @@ where impl CostEstimation for KzgAs where M: MultiMillerLoop, - M::Scalar: PrimeField, + M::Fr: PrimeField, { - type Input = Vec>; + type Input = Vec>; - fn estimate_cost(queries: &Vec>) -> Cost { + fn estimate_cost(queries: &Vec>) -> Cost { let num_w = query_sets(queries).len(); Cost { num_commitment: num_w, diff --git a/snark-verifier/src/system/halo2.rs b/snark-verifier/src/system/halo2.rs index d3ade0f1..231381ec 100644 --- a/snark-verifier/src/system/halo2.rs +++ b/snark-verifier/src/system/halo2.rs @@ -24,6 +24,9 @@ pub mod transcript; #[cfg(test)] pub(crate) mod test; +#[cfg(feature = "mv-lookup")] +use itertools::izip; + /// Configuration for converting a [`VerifyingKey`] of [`halo2_proofs`] into /// [`PlonkProtocol`]. #[derive(Clone, Debug, Default)] @@ -187,6 +190,27 @@ impl From for Rotation { } } +#[cfg(feature = "mv-lookup")] +struct Polynomials<'a, F: PrimeField> { + cs: &'a ConstraintSystem, + zk: bool, + query_instance: bool, + num_proof: usize, + degree: usize, + num_fixed: usize, + num_permutation_fixed: usize, + num_instance: Vec, + num_advice: Vec, + num_challenge: Vec, + advice_index: Vec, + challenge_index: Vec, + num_lookup_m: usize, + permutation_chunk_size: usize, + num_permutation_z: usize, + num_lookup_phi: usize, +} + +#[cfg(not(feature = "mv-lookup"))] struct Polynomials<'a, F: PrimeField> { cs: &'a ConstraintSystem, zk: bool, @@ -243,27 +267,53 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { let (num_challenge, challenge_index) = remapping(cs.challenge_phase()); assert_eq!(num_advice.iter().sum::(), cs.num_advice_columns()); assert_eq!(num_challenge.iter().sum::(), cs.num_challenges()); - - Self { - cs, - zk, - query_instance, - num_proof, - degree, - num_fixed: cs.num_fixed_columns(), - num_permutation_fixed: cs.permutation().get_columns().len(), - num_instance, - num_advice, - num_challenge, - advice_index, - challenge_index, - num_lookup_permuted: 2 * cs.lookups().len(), - permutation_chunk_size, - num_permutation_z: Integer::div_ceil( - &cs.permutation().get_columns().len(), - &permutation_chunk_size, - ), - num_lookup_z: cs.lookups().len(), + #[cfg(feature = "mv-lookup")] + { + Self { + cs, + zk, + query_instance, + num_proof, + degree, + num_fixed: cs.num_fixed_columns(), + num_permutation_fixed: cs.permutation().get_columns().len(), + num_instance, + num_advice, + num_challenge, + advice_index, + challenge_index, + num_lookup_m: cs.lookups().len(), + permutation_chunk_size, + num_permutation_z: Integer::div_ceil( + &cs.permutation().get_columns().len(), + &permutation_chunk_size, + ), + num_lookup_phi: cs.lookups().len(), + } + } + #[cfg(not(feature = "mv-lookup"))] + { + Self { + cs, + zk, + query_instance, + num_proof, + degree, + num_fixed: cs.num_fixed_columns(), + num_permutation_fixed: cs.permutation().get_columns().len(), + num_instance, + num_advice, + num_challenge, + advice_index, + challenge_index, + num_lookup_permuted: 2 * cs.lookups().len(), + permutation_chunk_size, + num_permutation_z: Integer::div_ceil( + &cs.permutation().get_columns().len(), + &permutation_chunk_size, + ), + num_lookup_z: cs.lookups().len(), + } } } @@ -278,6 +328,23 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { .collect() } + #[cfg(feature = "mv-lookup")] + fn num_witness(&self) -> Vec { + iter::empty() + .chain( + self.num_advice + .clone() + .iter() + .map(|num| self.num_proof * num), + ) + .chain([ + self.num_proof * self.num_lookup_m, + self.num_proof * (self.num_permutation_z + self.num_lookup_phi) + self.zk as usize, + ]) + .collect() + } + + #[cfg(not(feature = "mv-lookup"))] fn num_witness(&self) -> Vec { iter::empty() .chain( @@ -288,7 +355,7 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { ) .chain([ self.num_proof * self.num_lookup_permuted, - self.num_proof * (self.num_permutation_z + self.num_lookup_z) + self.zk as usize, + self.num_proof * self.num_permutation_z + self.zk as usize, ]) .collect() } @@ -421,6 +488,15 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { } } + #[cfg(feature = "mv-lookup")] + fn lookup_poly(&'a self, t: usize, i: usize) -> (usize, usize) { + let m = self.cs_witness_offset() + (t * self.num_lookup_m + i); + let phi = + m + self.num_witness()[self.num_advice.len()] + self.num_proof * self.num_permutation_z; + (m, phi) + } + + #[cfg(not(feature = "mv-lookup"))] fn lookup_poly(&'a self, t: usize, i: usize) -> (usize, usize, usize) { let permuted_offset = self.cs_witness_offset(); let z_offset = permuted_offset @@ -432,6 +508,18 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { (z, permuted_input, permuted_table) } + #[cfg(feature = "mv-lookup")] + fn lookup_queries( + &'a self, + t: usize, + ) -> impl IntoIterator + 'a { + (0..self.num_lookup_phi).flat_map(move |i| { + let (m, phi) = self.lookup_poly(t, i); + [Query::new(phi, 0), Query::new(phi, 1), Query::new(m, 0)] + }) + } + + #[cfg(not(feature = "mv-lookup"))] fn lookup_queries( &'a self, t: usize, @@ -659,6 +747,74 @@ impl<'a, F: PrimeField> Polynomials<'a, F> { .collect_vec() } + #[cfg(feature = "mv-lookup")] + fn lookup_constraints(&'a self, t: usize) -> impl IntoIterator> + 'a { + let l_0 = &Expression::::CommonPolynomial(CommonPolynomial::Lagrange(0)); + let l_last = &self.l_last(); + let l_active = &self.l_active(); + let beta = &self.beta(); + + let polys = (0..self.num_lookup_phi) + .map(|i| { + let (m, phi) = self.lookup_poly(t, i); + ( + Expression::::Polynomial(Query::new(phi, 0)), + Expression::::Polynomial(Query::new(phi, 1)), + Expression::::Polynomial(Query::new(m, 0)), + ) + }) + .collect_vec(); + + let compress = |expressions: &'a [plonk::Expression]| { + Expression::DistributePowers( + expressions + .iter() + .map(|expression| self.convert(expression, t)) + .collect(), + self.theta().into(), + ) + }; + + self.cs + .lookups() + .iter() + .zip(polys.iter()) + .flat_map(|(lookup, (phi, phi_omega, m))| { + let inputs = lookup + .input_expressions() + .iter() + .map(|expressions| compress(expressions) + beta) + .collect_vec(); + let table = &(compress(lookup.table_expressions()) + beta); + iter::empty() + .chain(Some(l_0 * phi)) + .chain(self.zk.then(|| l_last * phi)) + .chain(Some(if self.zk { + let input_prod = &inputs.iter().cloned().product::>(); + let lhs = table * input_prod * (phi_omega - phi); + let rhs = (inputs.len() > 1) + .then(|| { + (0..inputs.len()) + .map(|i| { + izip!(0.., &inputs) + .filter_map(|(j, input)| (i != j).then_some(input)) + .cloned() + .product() + }) + .sum::>() + * table + }) + .unwrap_or_else(|| table.clone()) + - m * input_prod; + l_active * (lhs - rhs) + } else { + unimplemented!() + })) + }) + .collect_vec() + } + + #[cfg(not(feature = "mv-lookup"))] fn lookup_constraints(&'a self, t: usize) -> impl IntoIterator> + 'a { let one = &Expression::Constant(F::ONE); let l_0 = &Expression::::CommonPolynomial(CommonPolynomial::Lagrange(0)); diff --git a/snark-verifier/src/system/halo2/test/kzg.rs b/snark-verifier/src/system/halo2/test/kzg.rs index 107af76e..e124c8b1 100644 --- a/snark-verifier/src/system/halo2/test/kzg.rs +++ b/snark-verifier/src/system/halo2/test/kzg.rs @@ -2,15 +2,12 @@ use crate::{ system::halo2::test::{read_or_create_srs, MainGateWithRange}, util::arithmetic::{fe_to_limbs, CurveAffine, MultiMillerLoop, PrimeField}, }; -use halo2_curves::serde::SerdeObject; +use halo2_curves::{serde::SerdeObject, CurveExt}; use halo2_proofs::poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; mod native; -#[cfg(feature = "loader_evm")] -mod evm; - #[cfg(feature = "loader_halo2")] mod halo2; @@ -21,17 +18,20 @@ pub const BITS: usize = 68; pub fn setup(k: u32) -> ParamsKZG where - M::Scalar: PrimeField, + M::Fr: PrimeField, + M::G1Affine: SerdeObject + CurveAffine, + M::G1: CurveExt, { ParamsKZG::::setup(k, ChaCha20Rng::from_seed(Default::default())) } pub fn main_gate_with_range_with_mock_kzg_accumulator( -) -> MainGateWithRange +) -> MainGateWithRange where - M::Scalar: PrimeField, - M::G1Affine: SerdeObject, - M::G2Affine: SerdeObject, + M::Fr: PrimeField, + M::G2Affine: SerdeObject + CurveAffine, + M::G1Affine: SerdeObject + CurveAffine, + M::G1: CurveExt, { let srs = read_or_create_srs(TESTDATA_DIR, 1, setup::); let [g1, s_g1] = [srs.get_g()[0], srs.get_g()[1]].map(|point| point.coordinates().unwrap()); diff --git a/snark-verifier/src/system/halo2/test/kzg/evm.rs b/snark-verifier/src/system/halo2/test/kzg/evm.rs deleted file mode 100644 index 3ec00032..00000000 --- a/snark-verifier/src/system/halo2/test/kzg/evm.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::{ - loader::native::NativeLoader, - pcs::kzg::{Bdfg21, Gwc19, KzgAs, LimbsEncoding}, - system::halo2::{ - test::{ - kzg::{ - self, halo2_kzg_config, halo2_kzg_create_snark, halo2_kzg_native_verify, - halo2_kzg_prepare, main_gate_with_range_with_mock_kzg_accumulator, BITS, LIMBS, - }, - StandardPlonk, - }, - transcript::evm::{ChallengeEvm, EvmTranscript}, - }, - verifier::plonk::PlonkVerifier, -}; -use halo2_curves::bn256::{Bn256, G1Affine}; -use halo2_proofs::poly::kzg::multiopen::{ProverGWC, ProverSHPLONK, VerifierGWC, VerifierSHPLONK}; -use paste::paste; -use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; - -macro_rules! halo2_kzg_evm_verify { - ($plonk_verifier:ty, $params:expr, $protocol:expr, $instances:expr, $proof:expr) => {{ - use halo2_curves::bn256::{Bn256, Fq, Fr}; - use halo2_proofs::poly::commitment::ParamsProver; - use std::rc::Rc; - use $crate::{ - loader::evm::{compile_solidity, deploy_and_call, encode_calldata, EvmLoader}, - system::halo2::{ - test::kzg::{BITS, LIMBS}, - transcript::evm::EvmTranscript, - }, - util::Itertools, - verifier::SnarkVerifier, - }; - - let loader = EvmLoader::new::(); - let deployment_code = { - let vk = ($params.get_g()[0].into(), $params.g2(), $params.s_g2()).into(); - let protocol = $protocol.loaded(&loader); - let mut transcript = EvmTranscript::<_, Rc, _, _>::new(&loader); - let instances = transcript.load_instances( - $instances - .iter() - .map(|instances| instances.len()) - .collect_vec(), - ); - let proof = - <$plonk_verifier>::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); - <$plonk_verifier>::verify(&vk, &protocol, &instances, &proof).unwrap(); - - compile_solidity(&loader.solidity_code()) - }; - - let calldata = encode_calldata($instances, &$proof); - let gas_cost = deploy_and_call(deployment_code.clone(), calldata.clone()).unwrap(); - println!("Total gas cost: {}", gas_cost); - - let mut calldata = calldata; - calldata[0] = calldata[0].wrapping_add(1); - assert!(deploy_and_call(deployment_code, calldata) - .unwrap_err() - .starts_with("Contract call transaction reverts")) - }}; -} - -macro_rules! test { - (@ $(#[$attr:meta],)* $prefix:ident, $name:ident, $k:expr, $config:expr, $create_circuit:expr, $prover:ty, $verifier:ty, $plonk_verifier:ty) => { - paste! { - $(#[$attr])* - fn []() { - let (params, pk, protocol, circuits) = halo2_kzg_prepare!( - $k, - $config, - $create_circuit - ); - let snark = halo2_kzg_create_snark!( - $prover, - $verifier, - EvmTranscript, - EvmTranscript, - ChallengeEvm<_>, - ¶ms, - &pk, - &protocol, - &circuits - ); - halo2_kzg_native_verify!( - $plonk_verifier, - params, - &snark.protocol, - &snark.instances, - &mut EvmTranscript::<_, NativeLoader, _, _>::new(snark.proof.as_slice()) - ); - halo2_kzg_evm_verify!( - $plonk_verifier, - params, - &snark.protocol, - &snark.instances, - snark.proof - ); - } - } - }; - ($name:ident, $k:expr, $config:expr, $create_circuit:expr) => { - test!(@ #[test], shplonk, $name, $k, $config, $create_circuit, ProverSHPLONK<_>, VerifierSHPLONK<_>, PlonkVerifier, LimbsEncoding>); - test!(@ #[test], plonk, $name, $k, $config, $create_circuit, ProverGWC<_>, VerifierGWC<_>, PlonkVerifier, LimbsEncoding>); - }; - ($(#[$attr:meta],)* $name:ident, $k:expr, $config:expr, $create_circuit:expr) => { - test!(@ #[test] $(,#[$attr])*, plonk, $name, $k, $config, $create_circuit, ProverGWC<_>, VerifierGWC<_>, PlonkVerifier, LimbsEncoding>); - }; -} - -test!( - zk_standard_plonk_rand, - 9, - halo2_kzg_config!(true, 1), - StandardPlonk::rand(ChaCha20Rng::from_seed(Default::default())) -); -test!( - zk_main_gate_with_range_with_mock_kzg_accumulator, - 9, - halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), - main_gate_with_range_with_mock_kzg_accumulator::() -); -test!( - #[cfg(feature = "loader_halo2")], - #[ignore = "cause it requires 32GB memory to run"], - zk_accumulation_two_snark, - 22, - halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), - kzg::halo2::Accumulation::two_snark() -); -test!( - #[cfg(feature = "loader_halo2")], - #[ignore = "cause it requires 32GB memory to run"], - zk_accumulation_two_snark_with_accumulator, - 22, - halo2_kzg_config!(true, 1, (0..4 * LIMBS).map(|idx| (0, idx)).collect()), - kzg::halo2::Accumulation::two_snark_with_accumulator() -); diff --git a/snark-verifier/src/system/halo2/transcript/evm.rs b/snark-verifier/src/system/halo2/transcript/evm.rs index 026c4295..98e56316 100644 --- a/snark-verifier/src/system/halo2/transcript/evm.rs +++ b/snark-verifier/src/system/halo2/transcript/evm.rs @@ -39,7 +39,7 @@ pub struct EvmTranscript, S, B> { impl EvmTranscript, usize, MemoryChunk> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { /// Initialize [`EvmTranscript`] given [`Rc`] and pre-allocate an /// u256 for `transcript_initial_state`. @@ -76,7 +76,7 @@ where impl Transcript> for EvmTranscript, usize, MemoryChunk> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn loader(&self) -> &Rc { &self.loader @@ -139,7 +139,7 @@ where impl TranscriptRead> for EvmTranscript, usize, MemoryChunk> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn read_scalar(&mut self) -> Result { let scalar = self.loader.calldataload_scalar(self.stream); @@ -175,7 +175,7 @@ where impl Transcript for EvmTranscript> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn loader(&self) -> &NativeLoader { &native::LOADER @@ -224,7 +224,7 @@ where impl TranscriptRead for EvmTranscript> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, S: Read, { fn read_scalar(&mut self) -> Result { @@ -233,7 +233,7 @@ where .read_exact(data.as_mut()) .map_err(|err| Error::Transcript(err.kind(), err.to_string()))?; data.reverse(); - let scalar = C::Scalar::from_repr_vartime(data).ok_or_else(|| { + let scalar = C::Scalar::from_repr_vartime(data.into()).ok_or_else(|| { Error::Transcript( io::ErrorKind::Other, "Invalid scalar encoding in proof".to_string(), @@ -289,12 +289,12 @@ where pub struct ChallengeEvm(C::Scalar) where C: CurveAffine, - C::Scalar: PrimeField; + C::Scalar: PrimeField>; impl EncodedChallenge for ChallengeEvm where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { type Input = [u8; 32]; @@ -311,7 +311,7 @@ impl halo2_proofs::transcript::Transcript> for EvmTranscript> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn squeeze_challenge(&mut self) -> ChallengeEvm { ChallengeEvm(Transcript::squeeze_challenge(self)) @@ -338,7 +338,7 @@ impl halo2_proofs::transcript::TranscriptRead> for EvmTranscript> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn read_point(&mut self) -> io::Result { match TranscriptRead::read_ec_point(self) { @@ -361,7 +361,7 @@ impl halo2_proofs::transcript::TranscriptReadBuffer> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(reader: R) -> Self { Self::new(reader) @@ -372,7 +372,7 @@ impl halo2_proofs::transcript::TranscriptWrite> for EvmTranscript> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn write_point(&mut self, ec_point: C) -> io::Result<()> { halo2_proofs::transcript::Transcript::>::common_point(self, ec_point)?; @@ -402,7 +402,7 @@ impl halo2_proofs::transcript::TranscriptWriterBuffer> where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(writer: W) -> Self { Self::new(writer) diff --git a/snark-verifier/src/util.rs b/snark-verifier/src/util.rs index 508c0eeb..fbe5b5d6 100644 --- a/snark-verifier/src/util.rs +++ b/snark-verifier/src/util.rs @@ -6,7 +6,7 @@ pub mod msm; pub mod poly; pub mod transcript; -pub(crate) use itertools::Itertools; +pub(crate) use itertools::{izip, Itertools}; #[cfg(feature = "parallel")] pub(crate) use rayon::current_num_threads; diff --git a/snark-verifier/src/verifier/plonk/protocol.rs b/snark-verifier/src/verifier/plonk/protocol.rs index e0a3147a..49574bee 100644 --- a/snark-verifier/src/verifier/plonk/protocol.rs +++ b/snark-verifier/src/verifier/plonk/protocol.rs @@ -10,7 +10,7 @@ use std::{ cmp::max, collections::{BTreeMap, BTreeSet}, fmt::Debug, - iter::{self, Sum}, + iter::{self, Product, Sum}, ops::{Add, Mul, Neg, Sub}, }; @@ -502,10 +502,17 @@ impl Neg for &Expression { } } -impl Sum for Expression { +impl Sum for Expression { fn sum>(iter: I) -> Self { iter.reduce(|acc, item| acc + item) - .unwrap_or_else(|| Expression::Constant(F::default())) + .unwrap_or_else(|| Expression::Constant(F::ZERO)) + } +} + +impl Product for Expression { + fn product>(iter: I) -> Self { + iter.reduce(|acc, item| acc * item) + .unwrap_or_else(|| Expression::Constant(F::ONE)) } }