diff --git a/.gitignore b/.gitignore index 2140562..552ab3e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build vk_deployment/ .aptos/ +.vscode/ diff --git a/README.md b/README.md index adf1483..c6008be 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -## halo2 verifier in Move +# halo2 verifier in Move -The project is a halo2 verifier written in Move language. +The project is a halo2 verifier written in Move language. Its objective to enhance the capability of the blockchains in the Move ecosystem by enabling halo2 zero-knowledge proofs to be verified on-chain. -### Why the project +## Why the project Halo2 is a widely used plonk implementation. zcash, scroll, axiom, taiko and many other famous projects are developed based on halo2. -There exists a general verification process for different circuits in rust, -however, blockchains cannot utilize the code directly because of the gap in the language or api. -several organizations are developing onchain verifiers(like [halo-solidity-verifier](https://github.com/privacy-scaling-explorations/halo2-solidity-verifier)) in solidity for EVM communities, -although it’s not a general verifier code, only a general template generator. +There exists a general verification process for different circuits in rust, +however, blockchains cannot utilize the code directly because of the gap in the language or api. +several organizations are developing onchain verifiers(like [halo-solidity-verifier](https://github.com/privacy-scaling-explorations/halo2-solidity-verifier)) in solidity for EVM communities, +although it’s not a general verifier code, only a general template generator. One still needs to generate a verifier contract for each circuit sharing most of the code. halo2-verifier.move uses a different approach, it tries to extract the information of a halo2 circuit, and abstract them out into a `Protocol` of the circuit(we call it the shape of a circuit), circuit’s shape includes: @@ -22,6 +22,6 @@ halo2-verifier.move uses a different approach, it tries to extract the informati With these information, the general verifier can read commitments and evaluations in proofs of the circuit, and do verification accordingly using a polynomial commitment scheme. -### Give it a try +## Give it a try See [TUTORIAL.md](./TUTORIAL.md). diff --git a/TUTORIAL.md b/TUTORIAL.md index 3ee1a47..4a1d510 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,11 +1,10 @@ -### Tutorial on halo2-verifier +# Tutorial on halo2-verifier This doc will guide you through the usage of halo2-verifier. -Cli `aptos` is necessary for the tutorial, make sure you download it from https://github.com/aptos-labs/aptos-core/releases. +Cli `aptos` is necessary for the tutorial, make sure you download it from . Also, the repo is an essence. - First, let's create an aptos profile on aptos devnet. Execute the command under the project root. It will generate config file of aptos account in `.aptos/config.yaml`. @@ -16,6 +15,7 @@ aptos init --network devnet Next, we need to publish the halo2-verifier & halo2-common modules to aptos devnet under the new-generated profile account. The following commands will use aptos cli to compile and publish the three packages: `common`, `verifier` and `api`. When first executing, it will fetch dependencies, which may need a little time. so be patient here. + ``` shell cd packages/common aptos move publish --skip-fetch-latest-git-deps --named-addresses halo2_common=default @@ -31,14 +31,16 @@ But first, we need a params of kzg setup. There exists a setup called [Perpetual We're going to use a version of axiom. the existing param file `crates/vk-gen-examples/params/challenge_0078-kzg_bn254_16.srs` is downloaded from [axiom page](https://docs.axiom.xyz/transparency-and-security/kzg-trusted-setup). -To view the kzg setup params, run the following cargo commands under directory `crates/vk-gen-examples`. +To view the kzg setup params, run the following cargo commands under directory `crates/vk-gen-examples`. It will output the g1, g2, and s_g2. + ```shell cargo run --release -- --param-path params/challenge_0078-kzg_bn254_16.srs view-param ``` We have to send a create-params transcation to make the params available on aptos. -Copy paste the `g1`, `g2`, `s_g2` as aptos args, resulting the following aptos command: +Copy paste the `g1`, `g2`, `s_g2` as aptos args, resulting the following aptos command: + ```shell aptos move run --function-id default::param_store::create --args hex:0x0100000000000000000000000000000000000000000000000000000000000000 hex:0xedf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19 hex:e4115200acc86e7670c83ded726335def098657fe8668323e9e41e6781b83b0a9d83b54bbb00215323ce6d7f9d7f331a286d7707d03f7dbdd3125c6163588d13 ``` @@ -46,17 +48,20 @@ aptos move run --function-id default::param_store::create --args hex:0x010000000 Then, we are ready to publish a circuit! We're going to use example `vector-mul` in our vk-gen-examples. Enter directory `crates/vk-gen-examples`, and run the cargo command, remember **replace the verifier-address with your aptos profile's address!** + ```shell cargo run --release -- --param-path params/challenge_0078-kzg_bn254_16.srs --verifier-address c9666cf9a032e81737eb706ce538a423706d86a2a502027fbc909e0817bf313b build-publish-vk-aptos-txn --example vector-mul ``` + It will output a json file which you can take as input to `aptos move run`. ```shell aptos move run --json-file VectorMul-publish-circuit.json ``` -Now, the circuit is published. We'll build a verify proof aptos txn and run it on aptos. +Now, the circuit is published. We'll build a verify proof aptos txn and run it on aptos. Run the command and replace the `verifier-address`/`param-address`/`circuit-address` with your aptos profile's address! + ```shell cargo run --release -- --param-path params/challenge_0078-kzg_bn254_16.srs --verifier-address c9666cf9a032e81737eb706ce538a423706d86a2a502027fbc909e0817bf313b build-verify-proof-aptos-txn --example vector-mul --kzg gwc --param-address c9666cf9a032e81737eb706ce538a423706d86a2a502027fbc909e0817bf313b --circuit-address c9666cf9a032e81737eb706ce538a423706d86a2a502027fbc909e0817bf313b ``` diff --git a/crates/verifier-sdk/Cargo.lock b/crates/verifier-sdk/Cargo.lock index 4e352ee..5be7780 100644 --- a/crates/verifier-sdk/Cargo.lock +++ b/crates/verifier-sdk/Cargo.lock @@ -211,8 +211,8 @@ dependencies = [ [[package]] name = "halo2_proofs" -version = "0.2.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v2023_04_20#be955686f86eb618f55d2320c0e042485b313d22" +version = "0.3.0" +source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v0.3.0#73408a140737d8336490452193b21f5a7a94e7de" dependencies = [ "blake2b_simd", "ff", @@ -227,18 +227,22 @@ dependencies = [ [[package]] name = "halo2curves" -version = "0.3.2" -source = "git+https://github.com/privacy-scaling-explorations/halo2curves?tag=0.3.2#9f5c50810bbefe779ee5cf1d852b2fe85dc35d5e" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db81d01d0bbfec9f624d7590fc6929ee2537a64ec1e080d8f8c9e2d2da291405" dependencies = [ + "blake2b_simd", "ff", "group", "lazy_static", "num-bigint", "num-traits", + "pairing", "pasta_curves", "paste", "rand", "rand_core", + "rayon", "static_assertions", "subtle", ] @@ -340,6 +344,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + [[package]] name = "pasta_curves" version = "0.5.1" diff --git a/crates/verifier-sdk/Cargo.toml b/crates/verifier-sdk/Cargo.toml index fb6d870..37f2b62 100644 --- a/crates/verifier-sdk/Cargo.toml +++ b/crates/verifier-sdk/Cargo.toml @@ -22,11 +22,11 @@ rand_core = { version = "0.6" } serde = { version = "1" } serde_json = { version = "1" } hex = { version = "0.4.3" } -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } bcs = { version = "0.1.6" } blake2b_simd = { version = "1" } multipoly = {path = "./multipoly" } -shape-generator={path="./shape-generator"} \ No newline at end of file +shape-generator={path="./shape-generator"} diff --git a/crates/verifier-sdk/aptos-verifier-api/src/proving.rs b/crates/verifier-sdk/aptos-verifier-api/src/proving.rs index 0f8a560..a172da9 100644 --- a/crates/verifier-sdk/aptos-verifier-api/src/proving.rs +++ b/crates/verifier-sdk/aptos-verifier-api/src/proving.rs @@ -1,4 +1,6 @@ -use halo2_proofs::halo2curves::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; +use halo2_proofs::arithmetic::CurveAffine; +use halo2_proofs::halo2curves::CurveExt; +use halo2_proofs::halo2curves::ff::{FromUniformBytes, WithSmallOrderMulGroup}; use halo2_proofs::halo2curves::pairing::{Engine, MultiMillerLoop}; use halo2_proofs::halo2curves::serde::SerdeObject; use halo2_proofs::plonk::{create_proof, verify_proof, Circuit, ProvingKey}; @@ -16,19 +18,18 @@ use rand_core::OsRng; pub fn prove_with_gwc_and_keccak256( circuit: ConcreteCircuit, - instance: &[&[E::Scalar]], + instance: &[&[E::Fr]], params: &ParamsKZG, pk: ProvingKey, ) -> Vec where E: Engine + Debug + MultiMillerLoop, - E::G1Affine: SerdeObject, - E::G2Affine: SerdeObject, - ConcreteCircuit: Circuit, - ::Scalar: PrimeField, - ::Scalar: Ord, - ::Scalar: WithSmallOrderMulGroup<3>, - ::Scalar: FromUniformBytes<64>, + E::G1Affine: + SerdeObject + CurveAffine::Fr, CurveExt = ::G1>, + E::G1: CurveExt, + E::G2Affine: SerdeObject + CurveAffine, + ConcreteCircuit: Circuit, + ::Fr: Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { prove_vm_circuit::< KZGCommitmentScheme, diff --git a/crates/verifier-sdk/shape-generator/src/lib.rs b/crates/verifier-sdk/shape-generator/src/lib.rs index 1078e8e..5766c71 100644 --- a/crates/verifier-sdk/shape-generator/src/lib.rs +++ b/crates/verifier-sdk/shape-generator/src/lib.rs @@ -33,6 +33,7 @@ pub struct CircuitInfo { fixed_queries: Vec, permutation_columns: Vec, lookups: Vec>, + shuffles: Vec>, } #[derive(Debug)] @@ -86,6 +87,11 @@ pub struct Lookup { pub input_exprs: Vec>, pub table_exprs: Vec>, } +#[derive(Debug)] +pub struct Shuffle { + pub input_exprs: Vec>, + pub shuffle_exprs: Vec>, +} pub type MultiVariatePolynomial = SparsePolynomial; @@ -194,6 +200,40 @@ where .collect(), }) .collect(), + shuffles: cs + .shuffles() + .iter() + .map(|s| Shuffle { + input_exprs: s + .input_expressions() + .iter() + .map(|e| { + expression_transform( + cs, + e, + cs.advice_queries().len(), + cs.fixed_queries().len(), + cs.instance_queries().len(), + cs.challenge_phase().len(), + ) + }) + .collect(), + shuffle_exprs: s + .shuffle_expressions() + .iter() + .map(|e| { + expression_transform( + cs, + e, + cs.advice_queries().len(), + cs.fixed_queries().len(), + cs.instance_queries().len(), + cs.challenge_phase().len(), + ) + }) + .collect(), + }) + .collect(), max_num_query_of_advice_column: cs .advice_queries() .iter() diff --git a/crates/verifier-sdk/shape-generator/src/serialize.rs b/crates/verifier-sdk/shape-generator/src/serialize.rs index 193e0a0..9436b0f 100644 --- a/crates/verifier-sdk/shape-generator/src/serialize.rs +++ b/crates/verifier-sdk/shape-generator/src/serialize.rs @@ -23,6 +23,8 @@ pub struct SerializableCircuitInfo { gates: Vec, lookups_input_exprs: Vec>, lookups_table_exprs: Vec>, + shuffles_input_exprs: Vec>, + shuffles_exprs: Vec>, } type IndexedMultiVariatePolynomial = Vec<(u16, SparseTerm)>; @@ -55,6 +57,7 @@ impl From> for SerializableCircuitInfo { fixed_queries, permutation_columns, lookups, + shuffles, }: CircuitInfo, ) -> Self { let mut fields_pool: Vec = Vec::new(); @@ -109,6 +112,44 @@ impl From> for SerializableCircuitInfo { (inputs, tables) }, ); + let shuffles_len = shuffles.len(); + let (shuffles_input, shuffles_shuffle) = shuffles.into_iter().fold( + ( + Vec::with_capacity(shuffles_len), + Vec::with_capacity(shuffles_len), + ), + |(mut shuffle_inputs, mut shuffles), s| { + shuffle_inputs.push( + s.input_exprs + .into_iter() + .map(|expr| { + expr.terms + .iter() + .map(|(coff, t)| { + let new_coff = index_element(&mut fields_pool, *coff); + (new_coff as u16, t.clone()) + }) + .collect::>() + }) + .collect(), + ); + shuffles.push( + s.shuffle_exprs + .into_iter() + .map(|expr| { + expr.terms + .iter() + .map(|(coff, t)| { + let new_coff = index_element(&mut fields_pool, *coff); + (new_coff as u16, t.clone()) + }) + .collect::>() + }) + .collect(), + ); + (shuffle_inputs, shuffles) + }, + ); Self { k, @@ -127,6 +168,8 @@ impl From> for SerializableCircuitInfo { gates, lookups_input_exprs: inputs, lookups_table_exprs: tables, + shuffles_input_exprs: shuffles_input, + shuffles_exprs: shuffles_shuffle, fields_pool, vk_transcript_repr, fixed_commitments, @@ -205,6 +248,16 @@ pub fn serialize( .iter() .map(serialize_lookup_exprs) .collect(); + let shuffles_input_exprs = circuit_info + .shuffles_input_exprs + .iter() + .map(serialize_lookup_exprs) + .collect(); + let shuffles_shuffle_exprs = circuit_info + .shuffles_exprs + .iter() + .map(serialize_lookup_exprs) + .collect(); Ok(vec![ general_info, advice_queries, @@ -215,6 +268,8 @@ pub fn serialize( gates, lookups_input_exprs, lookups_table_exprs, + shuffles_input_exprs, + shuffles_shuffle_exprs, ]) } diff --git a/crates/vk-gen-examples/Cargo.lock b/crates/vk-gen-examples/Cargo.lock index 361a013..915483d 100644 --- a/crates/vk-gen-examples/Cargo.lock +++ b/crates/vk-gen-examples/Cargo.lock @@ -454,8 +454,8 @@ dependencies = [ [[package]] name = "halo2_proofs" -version = "0.2.0" -source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v2023_04_20#be955686f86eb618f55d2320c0e042485b313d22" +version = "0.3.0" +source = "git+https://github.com/privacy-scaling-explorations/halo2.git?tag=v0.3.0#73408a140737d8336490452193b21f5a7a94e7de" dependencies = [ "blake2b_simd", "ff", @@ -470,18 +470,22 @@ dependencies = [ [[package]] name = "halo2curves" -version = "0.3.2" -source = "git+https://github.com/privacy-scaling-explorations/halo2curves?tag=0.3.2#9f5c50810bbefe779ee5cf1d852b2fe85dc35d5e" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db81d01d0bbfec9f624d7590fc6929ee2537a64ec1e080d8f8c9e2d2da291405" dependencies = [ + "blake2b_simd", "ff", "group", "lazy_static", "num-bigint", "num-traits", + "pairing", "pasta_curves", "paste", "rand", "rand_core", + "rayon", "static_assertions", "subtle", ] @@ -629,6 +633,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + [[package]] name = "pasta_curves" version = "0.5.1" diff --git a/crates/vk-gen-examples/Cargo.toml b/crates/vk-gen-examples/Cargo.toml index 6a46afa..aeb75ff 100644 --- a/crates/vk-gen-examples/Cargo.toml +++ b/crates/vk-gen-examples/Cargo.toml @@ -15,7 +15,7 @@ hex = { version = "0.4.3" } toml = { version = "0.8.8" } rand_core = { version = "0.6.4" } rand = { version = "0.8.5" } -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } ark-bn254 = { version = "0.4.0" } ark-serialize = { version = "0.4" } -ark-ec = { version = "0.4" } \ No newline at end of file +ark-ec = { version = "0.4" } diff --git a/crates/vk-gen-examples/src/examples/circuit_layout.rs b/crates/vk-gen-examples/src/examples/circuit_layout.rs index 4bb6519..c8a7cf3 100644 --- a/crates/vk-gen-examples/src/examples/circuit_layout.rs +++ b/crates/vk-gen-examples/src/examples/circuit_layout.rs @@ -10,6 +10,7 @@ use std::marker::PhantomData; /// This represents an advice column at a certain row in the ConstraintSystem #[derive(Copy, Clone, Debug)] +#[allow(dead_code)] pub struct Variable(Column, usize); #[derive(Clone)] diff --git a/crates/vk-gen-examples/src/examples/mod.rs b/crates/vk-gen-examples/src/examples/mod.rs index ccd5113..1be290f 100644 --- a/crates/vk-gen-examples/src/examples/mod.rs +++ b/crates/vk-gen-examples/src/examples/mod.rs @@ -1,6 +1,7 @@ pub mod circuit_layout; pub mod serialization; pub mod shuffle; +pub mod shuffle_api; pub mod simple_example; pub mod two_chip; pub mod vector_mul; diff --git a/crates/vk-gen-examples/src/examples/shuffle_api.rs b/crates/vk-gen-examples/src/examples/shuffle_api.rs new file mode 100644 index 0000000..49e0cf2 --- /dev/null +++ b/crates/vk-gen-examples/src/examples/shuffle_api.rs @@ -0,0 +1,232 @@ +use std::marker::PhantomData; + +use halo2_proofs::{ + arithmetic::{CurveAffine, Field}, + circuit::{Layouter, SimpleFloorPlanner, Value}, + halo2curves::ff::{FromUniformBytes, PrimeField}, + plonk::*, + poly::{ + commitment::ParamsProver, + ipa::{ + commitment::{IPACommitmentScheme, ParamsIPA}, + multiopen::{ProverIPA, VerifierIPA}, + strategy::AccumulatorStrategy, + }, + Rotation, VerificationStrategy, + }, + transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, + }, +}; +use rand_core::OsRng; + +struct ShuffleChip { + config: ShuffleConfig, + _marker: PhantomData, +} + +#[derive(Clone, Debug)] +pub struct ShuffleConfig { + input_0: Column, + input_1: Column, + shuffle_0: Column, + shuffle_1: Column, + s_input: Selector, + s_shuffle: Selector, +} + +impl ShuffleChip { + fn construct(config: ShuffleConfig) -> Self { + Self { + config, + _marker: PhantomData, + } + } + + fn configure( + meta: &mut ConstraintSystem, + input_0: Column, + input_1: Column, + shuffle_0: Column, + shuffle_1: Column, + ) -> ShuffleConfig { + let s_shuffle = meta.complex_selector(); + let s_input = meta.complex_selector(); + meta.shuffle("shuffle", |meta| { + let s_input = meta.query_selector(s_input); + let s_shuffle = meta.query_selector(s_shuffle); + let input_0 = meta.query_advice(input_0, Rotation::cur()); + let input_1 = meta.query_fixed(input_1, Rotation::cur()); + let shuffle_0 = meta.query_advice(shuffle_0, Rotation::cur()); + let shuffle_1 = meta.query_advice(shuffle_1, Rotation::cur()); + vec![ + (s_input.clone() * input_0, s_shuffle.clone() * shuffle_0), + (s_input * input_1, s_shuffle * shuffle_1), + ] + }); + ShuffleConfig { + input_0, + input_1, + shuffle_0, + shuffle_1, + s_input, + s_shuffle, + } + } +} + +#[derive(Default)] +pub struct MyCircuit { + input_0: Vec>, + input_1: Vec, + shuffle_0: Vec>, + shuffle_1: Vec>, +} + +impl Circuit for MyCircuit { + // Since we are using a single chip for everything, we can just reuse its config. + type Config = ShuffleConfig; + type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let input_0 = meta.advice_column(); + let input_1 = meta.fixed_column(); + let shuffle_0 = meta.advice_column(); + let shuffle_1 = meta.advice_column(); + ShuffleChip::configure(meta, input_0, input_1, shuffle_0, shuffle_1) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let ch = ShuffleChip::::construct(config); + layouter.assign_region( + || "load inputs", + |mut region| { + for (i, (input_0, input_1)) in + self.input_0.iter().zip(self.input_1.iter()).enumerate() + { + region.assign_advice(|| "input_0", ch.config.input_0, i, || *input_0)?; + region.assign_fixed( + || "input_1", + ch.config.input_1, + i, + || Value::known(*input_1), + )?; + ch.config.s_input.enable(&mut region, i)?; + } + Ok(()) + }, + )?; + layouter.assign_region( + || "load shuffles", + |mut region| { + for (i, (shuffle_0, shuffle_1)) in + self.shuffle_0.iter().zip(self.shuffle_1.iter()).enumerate() + { + region.assign_advice(|| "shuffle_0", ch.config.shuffle_0, i, || *shuffle_0)?; + region.assign_advice(|| "shuffle_1", ch.config.shuffle_1, i, || *shuffle_1)?; + ch.config.s_shuffle.enable(&mut region, i)?; + } + Ok(()) + }, + )?; + Ok(()) + } +} + +#[allow(dead_code)] +fn test_prover(k: u32, circuit: MyCircuit, expected: bool) +where + C::Scalar: FromUniformBytes<64>, +{ + let params = ParamsIPA::::new(k); + let vk = keygen_vk(¶ms, &circuit).unwrap(); + let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + + let proof = { + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + + create_proof::, ProverIPA, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&[]], + OsRng, + &mut transcript, + ) + .expect("proof generation should not fail"); + + transcript.finalize() + }; + + let accepted = { + let strategy = AccumulatorStrategy::new(¶ms); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + + verify_proof::, VerifierIPA, _, _, _>( + ¶ms, + pk.get_vk(), + strategy, + &[&[]], + &mut transcript, + ) + .map(|strategy| strategy.finalize()) + .unwrap_or_default() + }; + + assert_eq!(accepted, expected); +} + +pub fn get_example_circuit() -> MyCircuit { + let input_0 = [1, 2, 4, 1] + .map(|e: u64| Value::known(F::from(e))) + .to_vec(); + let input_1 = [10, 20, 40, 10].map(F::from).to_vec(); + let shuffle_0 = [4, 1, 1, 2] + .map(|e: u64| Value::known(F::from(e))) + .to_vec(); + let shuffle_1 = [40, 10, 10, 20] + .map(|e: u64| Value::known(F::from(e))) + .to_vec(); + MyCircuit { + input_0, + input_1, + shuffle_0, + shuffle_1, + } +} + +// #[test] +// fn test_shuffle_api() { +// use halo2_proofs::dev::MockProver; +// use halo2curves::pasta::Fp; +// const K: u32 = 4; +// let input_0 = [1, 2, 4, 1] +// .map(|e: u64| Value::known(Fp::from(e))) +// .to_vec(); +// let input_1 = [10, 20, 40, 10].map(Fp::from).to_vec(); +// let shuffle_0 = [4, 1, 1, 2] +// .map(|e: u64| Value::known(Fp::from(e))) +// .to_vec(); +// let shuffle_1 = [40, 10, 10, 20] +// .map(|e: u64| Value::known(Fp::from(e))) +// .to_vec(); +// let circuit = MyCircuit { +// input_0, +// input_1, +// shuffle_0, +// shuffle_1, +// }; +// let prover = MockProver::run(K, &circuit, vec![]).unwrap(); +// prover.assert_satisfied(); +// test_prover::(K, circuit, true); +// } diff --git a/crates/vk-gen-examples/src/main.rs b/crates/vk-gen-examples/src/main.rs index 45e1d02..9fce1a3 100644 --- a/crates/vk-gen-examples/src/main.rs +++ b/crates/vk-gen-examples/src/main.rs @@ -16,7 +16,7 @@ use std::env::current_dir; use std::fmt; use std::path::PathBuf; use vk_gen_examples::examples::{ - circuit_layout, serialization, shuffle, simple_example, two_chip, vector_mul, + circuit_layout, serialization, shuffle, shuffle_api, simple_example, two_chip, vector_mul, }; use vk_gen_examples::proof::{prove_with_keccak256, KZG}; @@ -65,6 +65,7 @@ pub enum Examples { CircuitLayout, Serialization, Shuffle, + ShuffleApi, SimpleExample, TwoChip, VectorMul, @@ -149,6 +150,10 @@ fn main() -> anyhow::Result<()> { let circuit = shuffle::get_example_circuit(); generate_circuit_info(¶ms, &circuit)? } + Examples::ShuffleApi => { + let circuit = shuffle_api::get_example_circuit(); + generate_circuit_info(¶ms, &circuit)? + } Examples::SimpleExample => { let circuit = simple_example::get_example_circuit(); generate_circuit_info(¶ms, &circuit.0)? @@ -226,6 +231,13 @@ fn main() -> anyhow::Result<()> { let proof = prove_with_keccak256(circuit, &[], ¶ms, pk, kzg); (proof, vec![]) } + Examples::ShuffleApi => { + let circuit = shuffle_api::get_example_circuit::(); + let vk = keygen_vk(¶ms, &circuit).unwrap(); + let pk = keygen_pk(¶ms, vk, &circuit).unwrap(); + let proof = prove_with_keccak256(circuit, &[], ¶ms, pk, kzg); + (proof, vec![]) + } Examples::SimpleExample => { let (circuit, instances) = simple_example::get_example_circuit::(); let vk = keygen_vk(¶ms, &circuit).unwrap(); diff --git a/crates/vk-gen-examples/src/proof.rs b/crates/vk-gen-examples/src/proof.rs index f602e49..26d0843 100644 --- a/crates/vk-gen-examples/src/proof.rs +++ b/crates/vk-gen-examples/src/proof.rs @@ -1,4 +1,6 @@ -use halo2_proofs::halo2curves::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; +use halo2_proofs::arithmetic::CurveAffine; +use halo2_proofs::halo2curves::CurveExt; +use halo2_proofs::halo2curves::ff::{FromUniformBytes, WithSmallOrderMulGroup}; use halo2_proofs::halo2curves::pairing::{Engine, MultiMillerLoop}; use halo2_proofs::halo2curves::serde::SerdeObject; use halo2_proofs::plonk::{create_proof, verify_proof, Circuit, ProvingKey}; @@ -38,20 +40,19 @@ impl std::fmt::Display for KZG { pub fn prove_with_keccak256( circuit: ConcreteCircuit, - instance: &[&[E::Scalar]], + instance: &[&[E::Fr]], params: &ParamsKZG, pk: ProvingKey, kzg: KZG, ) -> Vec where E: Engine + Debug + MultiMillerLoop, - E::G1Affine: SerdeObject, - E::G2Affine: SerdeObject, - ConcreteCircuit: Circuit, - ::Scalar: PrimeField, - ::Scalar: Ord, - ::Scalar: WithSmallOrderMulGroup<3>, - ::Scalar: FromUniformBytes<64>, + E::G1Affine: + SerdeObject + CurveAffine::Fr, CurveExt = ::G1>, + E::G1: CurveExt, + E::G2Affine: SerdeObject + CurveAffine, + ConcreteCircuit: Circuit, + ::Fr: Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { match kzg { KZG::GWC => prove_vm_circuit::< @@ -73,19 +74,18 @@ where pub fn prove_with_gwc_and_keccak256( circuit: ConcreteCircuit, - instance: &[&[E::Scalar]], + instance: &[&[E::Fr]], params: &ParamsKZG, pk: ProvingKey, ) -> Vec where E: Engine + Debug + MultiMillerLoop, - E::G1Affine: SerdeObject, - E::G2Affine: SerdeObject, - ConcreteCircuit: Circuit, - ::Scalar: PrimeField, - ::Scalar: Ord, - ::Scalar: WithSmallOrderMulGroup<3>, - ::Scalar: FromUniformBytes<64>, + E::G1Affine: + SerdeObject + CurveAffine::Fr, CurveExt = ::G1>, + E::G1: CurveExt, + E::G2Affine: SerdeObject + CurveAffine, + ConcreteCircuit: Circuit, + ::Fr: Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { prove_vm_circuit::< KZGCommitmentScheme, @@ -98,19 +98,18 @@ where pub fn prove_with_shplonk_and_keccak256( circuit: ConcreteCircuit, - instance: &[&[E::Scalar]], + instance: &[&[E::Fr]], params: &ParamsKZG, pk: ProvingKey, ) -> Vec where E: Engine + Debug + MultiMillerLoop, - E::G1Affine: SerdeObject, - E::G2Affine: SerdeObject, - ConcreteCircuit: Circuit, - ::Scalar: PrimeField, - ::Scalar: Ord, - ::Scalar: WithSmallOrderMulGroup<3>, - ::Scalar: FromUniformBytes<64>, + E::G1Affine: + SerdeObject + CurveAffine::Fr, CurveExt = ::G1>, + E::G1: CurveExt, + E::G2Affine: SerdeObject + CurveAffine, + ConcreteCircuit: Circuit, + ::Fr: Ord + WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { prove_vm_circuit::< KZGCommitmentScheme, diff --git a/packages/api/sources/verifier_api.move b/packages/api/sources/verifier_api.move index 0f9fa25..f3a614b 100644 --- a/packages/api/sources/verifier_api.move +++ b/packages/api/sources/verifier_api.move @@ -24,10 +24,13 @@ module verifier_api::verifier_api { gates: vector>, lookups_input_exprs: vector>, lookups_table_exprs: vector>, + shuffle_input_exprs: vector>, + shuffle_exprs: vector>, ) { let proto = protocol::from_bytes( general_info, advice_queries, instance_queries, fixed_queries, permutation_columns, - fields_pool, gates, lookups_input_exprs, lookups_table_exprs + fields_pool, gates, lookups_input_exprs, lookups_table_exprs, shuffle_input_exprs, + shuffle_exprs ); move_to(sender, Circuit { protocol: proto }); } diff --git a/packages/api/tests/verifier_api_test.move b/packages/api/tests/verifier_api_test.move index 1da57a5..feae833 100644 --- a/packages/api/tests/verifier_api_test.move +++ b/packages/api/tests/verifier_api_test.move @@ -15,6 +15,7 @@ module verifier_api::verifier_api_test { const TESTING_G2: vector = x"edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19"; #[test_only] const TESTING_S_G2: vector = x"e4115200acc86e7670c83ded726335def098657fe8668323e9e41e6781b83b0a9d83b54bbb00215323ce6d7f9d7f331a286d7707d03f7dbdd3125c6163588d13"; + #[test(s = @aptos_std)] public fun check_verify_ok(s: &signer) { crypto_algebra::enable_cryptography_algebra_natives(s); @@ -58,6 +59,8 @@ module verifier_api::verifier_api_test { x"0200000000000200000002000000010000000300000001000000010003000000000000000100000001000000010000000300000001000000" ], vector[], + vector[], + vector[], vector[] ); let proof = x"06661ff6e3bd260e8dc4f9096034eaf96782d3fed55ec7fbc5928d2a8de1dd5ce1563c6c4fbc544a0544f957202b6cdcfb0b2e203ba886694a19542d8f749c2d9d0fe09c16f5172e45e48f82f28679649c482ae4baacec0c4d5514711eb4aa255508673b9a378da619ab1d60b24a077e7d4489f19e8b6610542d92ff65bb8d6b5442ab04b452a5199776c980dafb6e6ef641b08ce72c0c1560256b552a2a0520ce48ece7c5dd11faa9945ef0cdaffe033d613b6ee01a9584338a37dc189b6954e0fb075b65df7afa31f16a23fdda32d53accf86e4ab33c64b9237f9c7717326717ecb8a5f8dcba798cb4706badbf1e2816921a2b14773e2421b18fb8e9e5004e23c79f2df17f131018a9b0043d6583779d740aa5df3da4076942d7e82e9f7a46b737c901814134214beda0ebb34a512dee5929153286f57c7dbab5e4ef738640d2f0b3401061c36c6eadb63c55aaa3357d0e593c6a7860f3a570c618a604871aa83767b4cb569b4cd16cb0ae0740cb6ebbcf6f18fd825430c1caaadb6805650d2cca4df11456e2853f2356b5307b523030a82b13582a0377076657bc1294492b5a6328fc5d9320b2c5ce1792d8b401e4382a1fdb0deadc5cdd447847d6b3b02b0bfd442c0c7db984d0d870967335e53f531b9139e6ec7df43f601a5258d3a016e94a45148d1cfd60395aa986e5c2c4bb8692aa57045c6f772829084bd69f65214f5434f2de996df3559d95f5048cd5a60349f00fa9c22310fc9970e23053f32d0717727826b697a042ae376557dfc4dd3830038888488e5a21d5ac7f9cf30025b912e075fa88e2ee505b178e122f63ee4c7219bd82d7fb80918f914f50b9e11cd89b4365123a7d8beb4529c371f102c03ca22a994e67ebd4ec2401703d33101d242d591ac833fc91c152aea87ae65d81d1c5d1bdd50901d2430dd3b38b1d4c0e091b929d16ded653eceb1de9ca6f3a53b0f8411906d48c796927e728003c81185015ceb7ca4f7fa5d8ef02f6864f1804b408261400ed716f0b05a33fead5e2233c0ca6d4c131ba8221d364ebaa9583375168c925e2fe9bbf98ff93fa2de3ec295d4f780f7df328d6012abcfa37ffe0f185bbe12e470e1ba71e663c581adab7129d8e7a313d1ca418b1f1d35d5dc9229a10bf3f7c560570f591da67f02194ac09332bd46942c8c4127ae6cc33bcef1515d1de2c84e46e1d12876cd44fbcb6c30f872e14bf0c7cfb3376603e2811898c5f9776b200f601fcc2de97fcd25e89c206c15744f748146054424611186f4e97485032fb74de6a770fd7489d3e89a7550092f1f97e9048a549c3f9ef2d618c002a5722c8a50f9f614a05284fd27a41771f828688983206e1c96c71db5544f0ece50eda4adfa3219355e1be504bf454f022f85eb36928354ef1c2a4d9cc891273f46f30c672342b81a76b1edf4571557d1c38326379583844110da99a079f256a8b8f5ab1b43ce7a03e12ddda4530a5ff61"; @@ -69,4 +72,56 @@ module verifier_api::verifier_api_test { verifier_api::verify_proof_gwc(signer::address_of(&signer), signer::address_of(&signer), vector::singleton(instances), proof); } + #[test(s = @aptos_std)] + public fun check_verify_shuffle_api_ok(s: &signer) { + crypto_algebra::enable_cryptography_algebra_natives(s); + let signers = unit_test::create_signers_for_testing(1); + let signer = vector::pop_back(&mut signers); + param_store::create(&signer, TESTING_G1, TESTING_G2, TESTING_S_G2); + // protocol of example shuffle-api + // generated by `cargo run --release -- --param-path params/challenge_0078-kzg_bn254_16.srs --verifier-address c5be6b4d9ca3f9bfbf4edd9a3cf434f2b136df23b69b66306e6a2d0378c34458 build-publish-vk-aptos-txn --example shuffle-api -o vk_deployment` + + verifier_api::publish_circuit(&signer, + vector[ + x"5ed91716521c30ca9c8322f2908211f5f1b6bb9bf47ee7439f9df6aac933dd2c", + x"bce0ce37c00b841d90697913f3b1653d3281fbc05a8f106075edd4597b138d0b74c80ac6072406d2d0ffba6dc4919f0fd1d570e6ace1a812bfa5d44a406a6a6374c80ac6072406d2d0ffba6dc4919f0fd1d570e6ace1a812bfa5d44a406a6a63", + x"", + x"10", + x"01000000", + x"04000000", + x"0300000000000000", + x"0000000000000000", + x"000000", + x"" + ], + vector[ + x"00000000000100000000", + x"00010000000100000000", + x"00020000000100000000" + ], + vector[], + vector[ + x"ff000000000100000000", + x"ff010000000100000000", + x"ff020000000100000000", + ], + vector[], + vector[ + x"0100000000000000000000000000000000000000000000000000000000000000" + ], + vector[], + vector[], + vector[], + vector[ + x"020000001a00000001000000000002000000000000000100000005000000010000001a0000000100000000000200000003000000010000000500000001000000" + ], + vector[ + x"020000001a00000001000000000002000000010000000100000004000000010000001a0000000100000000000200000002000000010000000400000001000000" + ], + ); + let proof = x"47e4688418f21da809dac262edf7975752d4193026b7858bd1949d67cbaf2242e90550f0211090a27c12a9ee7f41ca20be7ce3716993cdcd484bfec41f72e4003811bb70c6380e3333369277c49142f92041125372e2b10f961359f066ac9c4ccb2958288fc1a3ea7527f6bce63fc137496968c60b675a1b59ac93ac1a87a9146d8294889130db0b9d9e3e995b9676c2254cfb58672e39d7948cea3c33cfba44521a54fcc4d14b5525cc4a6ae7e39a25cd6f861473f5a9fd36a2a260f5636a192936b7cfc47b57a291054a0e1e8a3204c848bd00536c52d408ae6498054ac542189fc6e2dd2c011a5cc87f8e63016efb347f6097f4158fe19b1a5be70e07745659dddeea83fb47b26940fccfd0374150c4ea369e7dd44236c83256f127a35c218f4dbadbc38b0370608f4655b22f3487d748b09d063f4412ca5c8ef98855c0168c5048324febb92fb0e7201ffc62bf778e294ae041993decd2d1b56b0401ba0686d41b1718203e2103ea91b27059af95a0f0fd600aca2cdbfe5df953ef46b71e7c7b729d96365f2262b08840a67d3b2eb8974168639b7e3f3983890226f4830b7c7b729d96365f2262b08840a67d3b2eb8974168639b7e3f3983890226f4830b9143926c963976cc739a98af4138c679327f6013d16ab6067c11324bdc2e8e0b4c08d556023705ca21dfa6e8fe49e3c5432b3468dd9e8faf7dd9b20b4273950e50a01d2414610095c7fb53084b4c4ed6c7f34a629517223a20360d235e24db1aea988edfc997162778180b65144f31c74d18068b43e1244f908e2aabf0cc631f2cb6f5c44abb01ff47083ca0d68d3704bb342eeb56c9fc6ba6dd9fc1d39e7705"; + let instances = vector[]; + verifier_api::verify_proof_gwc(signer::address_of(&signer), signer::address_of(&signer), vector::singleton(instances), proof); + } + } diff --git a/packages/common/sources/bn254_utils.move b/packages/common/sources/bn254_utils.move index c3a319f..518653c 100644 --- a/packages/common/sources/bn254_utils.move +++ b/packages/common/sources/bn254_utils.move @@ -52,16 +52,16 @@ module halo2_common::bn254_utils { 871749566700742666, ]; - public fun sqrt_fq(self: &Element): Option> { - let tmp = pow(self, &FQ_SQRT_PRE_COMP); - if (crypto_algebra::eq(&crypto_algebra::sqr(&tmp), self)) { + public fun sqrt_fq(e: &Element): Option> { + let tmp = pow(e, &FQ_SQRT_PRE_COMP); + if (crypto_algebra::eq(&crypto_algebra::sqr(&tmp), e)) { option::some(tmp) } else { option::none() } } - fun pow(self: &Element, exp: &vector): Element { + fun pow(e: &Element, exp: &vector): Element { let result = crypto_algebra::one(); let j = vector::length(exp); // if we never meet bit with 1, don't bother to sqr. @@ -77,7 +77,7 @@ module halo2_common::bn254_utils { }; // the i bit is 1 if(((num >> i) & 1) == 1) { - result = crypto_algebra::mul(&result, self); + result = crypto_algebra::mul(&result, e); meet_one = true; }; @@ -91,7 +91,7 @@ module halo2_common::bn254_utils { } } - public fun pow_u32(self: &Element, num: u32): Element { + public fun pow_u32(e: &Element, num: u32): Element { let result = crypto_algebra::one(); let i = 32u8; // if we never meet bit with 1, don't bother to sqr. @@ -103,7 +103,7 @@ module halo2_common::bn254_utils { }; // the i bit is 1 if(((num >> i) & 1) == 1) { - result = crypto_algebra::mul(&result, self); + result = crypto_algebra::mul(&result, e); meet_one = true; }; @@ -237,16 +237,16 @@ module halo2_common::bn254_utils { crypto_algebra::deserialize(e) } - public fun eq_elements(self: &vector>, other: &vector>): bool { + public fun eq_elements(e: &vector>, other: &vector>): bool { - let elements_len = vector::length(self); + let elements_len = vector::length(e); if(elements_len != vector::length(other)) { return false }; let i = 0; while (i < elements_len) { - let e_1 = vector::borrow(self, i); + let e_1 = vector::borrow(e, i); let e_2 = vector::borrow(other, i); if(!crypto_algebra::eq(e_1, e_2)) { return false diff --git a/packages/verifier/sources/halo2_verifier.move b/packages/verifier/sources/halo2_verifier.move index a5b167c..aac90a9 100644 --- a/packages/verifier/sources/halo2_verifier.move +++ b/packages/verifier/sources/halo2_verifier.move @@ -16,8 +16,9 @@ module halo2_verifier::halo2_verifier { use halo2_verifier::gwc; use halo2_verifier::lookup::{Self, PermutationCommitments}; + use halo2_verifier::shuffle; use halo2_verifier::permutation; - use halo2_verifier::protocol::{Self, Protocol, instance_queries, num_challenges, Lookup, blinding_factors, num_advice_columns}; + use halo2_verifier::protocol::{Self, Protocol, instance_queries, num_challenges, Lookup, Shuffle, blinding_factors, num_advice_columns}; use halo2_verifier::transcript::{Self, Transcript}; use halo2_verifier::vanishing; use halo2_verifier::shplonk; @@ -128,6 +129,11 @@ module halo2_verifier::halo2_verifier { protocol::num_permutation_z(protocol) ); let lookups_committed = lookup_read_product_commitments(lookups_permuted, &mut transcript); + let shuffles_committed = shuffle_read_product_commitments( + &mut transcript, + num_proof, + protocol::num_shuffle(protocol) + ); let vanishing = vanishing::read_commitments_before_y(&mut transcript); let y = transcript::squeeze_challenge(&mut transcript); let vanishing = vanishing::read_commitments_after_y( @@ -224,6 +230,15 @@ module halo2_verifier::halo2_verifier { } ); + let shuffles_evaluated = map_ref, vector>( + &shuffles_committed, + |commited| { + map_ref(commited, |c| { + shuffle::evaluate(c, &mut transcript) + }) + } + ); + let vanishing = { // -(blinding_factor+1)..=0 let blinding_factors = blinding_factors(protocol); @@ -286,6 +301,18 @@ module halo2_verifier::halo2_verifier { &theta, &beta, &gamma, &mut expressions, ); + evaluate_shuffles( + vector::borrow(&shuffles_evaluated, i), + protocol::shuffles(protocol), + &coeff_pool, + vector::borrow(&advice_evals, i), + &fixed_evals, + vector::borrow(&instance_evals, i), + &challenges, + &l_0, &l_last, &l_blind, + &theta, &gamma, + &mut expressions, + ); i = i + 1; }; @@ -298,6 +325,7 @@ module halo2_verifier::halo2_verifier { { vector::reverse(&mut permutations_evaluated); vector::reverse(&mut lookups_evaluated); + vector::reverse(&mut shuffles_evaluated); let i = 0; while (i < num_proof) { queries(protocol, @@ -308,6 +336,7 @@ module halo2_verifier::halo2_verifier { vector::borrow(&advice_evals, i), vector::pop_back(&mut permutations_evaluated), vector::pop_back(&mut lookups_evaluated), + vector::pop_back(&mut shuffles_evaluated), ); i = i + 1; }; @@ -378,6 +407,26 @@ module halo2_verifier::halo2_verifier { map(lookups_permuted, |lookups| map(lookups, |l| lookup::read_product_commitment(l, transcript))) } + fun shuffle_read_product_commitments( + transcript: &mut Transcript, + num_proof: u64, + num_shuffle: u64 + ): vector> { + let shuffles = vector::empty(); // (A, S) + let i = 0; + while (i < num_proof) { + let j = 0; + let result = vector::empty(); + while (j < num_shuffle) { + vector::push_back(&mut result, shuffle::shuffles_read_product_commitments(transcript)); + j = j + 1; + }; + vector::push_back(&mut shuffles, result); + i = i + 1; + }; + shuffles + } + fun permutation_read_product_commitments( transcript: &mut Transcript, num_proof: u64, @@ -403,7 +452,9 @@ module halo2_verifier::halo2_verifier { advice_commitments: &vector>, advice_evals: &vector>, permutation: permutation::Evaluted, - lookups: vector + lookups: vector, + shuffles: vector, + ) { /* // instance queries @@ -437,6 +488,7 @@ module halo2_verifier::halo2_verifier { permutation::queries(permutation, queries, protocol, domain, x); lookup::queries(&lookups, queries, protocol, domain, x); + shuffle::queries(&shuffles, queries, protocol, domain, x); } fun evaluate_gates( @@ -482,4 +534,32 @@ module halo2_verifier::halo2_verifier { ); }); } + + fun evaluate_shuffles( + shuffle_evaluates: &vector, + shuffle: &vector, + coeff_pool: &vector>, + advice_evals: &vector>, + fixed_evals: &vector>, + instance_evals: &vector>, + challenges: &vector>, + l_0: &Element, + l_last: &Element, + l_blind: &Element, + theta: &Element, + gamma: &Element, + results: &mut vector>, + ) { + vector::zip_ref(shuffle_evaluates, shuffle, | shuffle_evaluate, s| { + shuffle::expression( + shuffle_evaluate, + s, + coeff_pool, + advice_evals, + fixed_evals, + instance_evals, challenges, l_0, l_last, l_blind, theta, gamma, + results + ); + }); + } } diff --git a/packages/verifier/sources/lookup.move b/packages/verifier/sources/lookup.move index d8baf0b..32aebc4 100644 --- a/packages/verifier/sources/lookup.move +++ b/packages/verifier/sources/lookup.move @@ -171,10 +171,10 @@ module halo2_verifier::lookup { acc } - public fun queries(self: &vector, queries: &mut vector, _protocol: &Protocol, domain: &Domain, x: &Element) { + public fun queries(e: &vector, queries: &mut vector, _protocol: &Protocol, domain: &Domain, x: &Element) { let x_inv = domain::rotate_omega(domain, x, &i32::neg_from(1)); let x_next = domain::rotate_omega(domain, x, &i32::from(1)); - for_each_ref(self, |evaluated| { + for_each_ref(e, |evaluated| { let eval: &Evaluated = evaluated; // Open lookup product commitment at x vector::push_back(queries, query::new_commitment(eval.commited.product_commitment, *x, eval.product_eval)); diff --git a/packages/verifier/sources/permutation.move b/packages/verifier/sources/permutation.move index 48a15ff..9fb0afd 100644 --- a/packages/verifier/sources/permutation.move +++ b/packages/verifier/sources/permutation.move @@ -216,7 +216,9 @@ module halo2_verifier::permutation { }); // Open it at \omega^{last} x for all but the last set - vector::pop_back(&mut self.sets); + if (vector::length(& self.sets) > 0) { + vector::pop_back(&mut self.sets); + }; let Evaluted { sets } = self; for_each_reverse(sets, |set| { let s: PermutationEvaluatedSet = set; diff --git a/packages/verifier/sources/protocol.move b/packages/verifier/sources/protocol.move index 080e18a..975b5c6 100644 --- a/packages/verifier/sources/protocol.move +++ b/packages/verifier/sources/protocol.move @@ -62,6 +62,7 @@ module halo2_verifier::protocol { fields_pool: vector>, gates: vector, lookups: vector, + shuffles: vector, } @@ -69,6 +70,10 @@ module halo2_verifier::protocol { input_expressions: vector, table_expressions: vector, } + struct Shuffle has store, drop { + input_expressions: vector, + shuffle_expressions: vector, + } // --- Protocol Deserialzation start --- @@ -84,6 +89,8 @@ module halo2_verifier::protocol { gates: vector>, lookups_input_exprs: vector>, lookups_table_exprs: vector>, + shuffles_input_exprs: vector>, + shuffles_exprs: vector>, ): Protocol { let challenge_phase = vector::pop_back(&mut general_info); let advice_column_phase = vector::pop_back(&mut general_info); @@ -119,6 +126,14 @@ module halo2_verifier::protocol { table_expressions: q, })); + let shuffles = vector::empty(); + let shuffles_input_exprs = vector::map_ref(&shuffles_input_exprs, |q| deserialize_lookup_exprs(q)); + let shuffles_exprs = vector::map_ref(&shuffles_exprs, |q| deserialize_lookup_exprs(q)); + vector::zip(shuffles_input_exprs, shuffles_exprs, |p, q| vector::push_back(&mut shuffles, Shuffle { + input_expressions: p, + shuffle_expressions: q, + })); + let protocol = Protocol { vk_transcript_repr: serialize_fr(&vk_repr), fixed_commitments: map_ref(&fixed_commitments, |c| serialize_g1(c)), @@ -137,6 +152,7 @@ module halo2_verifier::protocol { fields_pool, gates, lookups, + shuffles, }; protocol } @@ -300,6 +316,10 @@ module halo2_verifier::protocol { &protocol.lookups } + public fun shuffles(protocol: &Protocol): &vector { + &protocol.shuffles + } + public fun gates(protocol: &Protocol): &vector { &protocol.gates } @@ -316,6 +336,14 @@ module halo2_verifier::protocol { &self.table_expressions } + public fun shuffle_input_exprs(self: &Shuffle): &vector { + &self.input_expressions + } + + public fun shuffle_exprs(self: &Shuffle): &vector { + &self.shuffle_expressions + } + public fun blinding_factors(protocol: &Protocol): u64 { // All of the prover's advice columns are evaluated at no more than let factors = max((protocol.max_num_query_of_advice_column as u64), 1); @@ -406,6 +434,10 @@ module halo2_verifier::protocol { vector::length(&protocol.lookups) } + public fun num_shuffle(protocol: &Protocol): u64 { + vector::length(&protocol.shuffles) + } + public fun permutation_chunk_size(protocol: &Protocol): u32 { protocol.cs_degree - 2 } @@ -553,6 +585,12 @@ module halo2_verifier::protocol { vector[ x"0100000012000000010000000100010000000500000001000000" ], + vector[ + x"0100000012000000010000000100010000000000000001000000" + ], + vector[ + x"0100000012000000010000000100010000000500000001000000" + ], ); assert!(protocol.k == 0x10, 1); diff --git a/packages/verifier/sources/shplonk.move b/packages/verifier/sources/shplonk.move index b8becb3..cb12ec9 100644 --- a/packages/verifier/sources/shplonk.move +++ b/packages/verifier/sources/shplonk.move @@ -15,17 +15,17 @@ module halo2_verifier::shplonk { #[test_only] use std::option; - + struct CommitmentRotationSet has copy, drop { rotations: vector>, commitment: CommitmentReference, } - + struct RotationSetCommitment has copy, drop { rotations: vector>, commitments: vector, } - + struct Commitment has copy, drop { commitment: CommitmentReference, evals: vector>, @@ -157,7 +157,7 @@ module halo2_verifier::shplonk { }); }; }); - + // Implement btree for rotations in commitment_rotation_set_map vector::for_each_mut(&mut commitment_rotation_set_map, |set| { let set: &mut CommitmentRotationSet = set; @@ -195,12 +195,12 @@ module halo2_verifier::shplonk { let commitment: CommitmentReference = *commitment; let evals = vector::map_ref(&rotations, |rotation| { let rotation: Element = *rotation; - + let (_, index) = vector::find(queries, |q| { let q: &VerifierQuery = q; query::eq_commit_reference(&commitment, query::commitment(q)) && crypto_algebra::eq(&rotation, query::point(q)) }); - + *query::eval(vector::borrow(queries, index)) }); @@ -215,7 +215,7 @@ module halo2_verifier::shplonk { points: rotations } }); - + super_point_set = remove_duplicate_and_sort(&super_point_set); (rotation_sets, super_point_set) @@ -232,8 +232,10 @@ module halo2_verifier::shplonk { let denoms = vector::empty(); vector::enumerate_ref(&points, |j, x_j| { + // dummy code added to avoid compile issue. + let _j : u64 = j; let denom = vector::empty(); - + vector::enumerate_ref(&points, |k, x_k| { if (k != j) { vector::push_back(&mut denom, bn254_utils::invert(&crypto_algebra::sub(x_j, x_k))); @@ -289,7 +291,7 @@ module halo2_verifier::shplonk { let c = vector::borrow_mut(&mut product, t); *c = crypto_algebra::add(&crypto_algebra::mul(a, &crypto_algebra::mul(&crypto_algebra::neg(denom), x_k)), &crypto_algebra::mul(b, denom)); - + t = t + 1; }; @@ -312,7 +314,7 @@ module halo2_verifier::shplonk { let interpolation_coeff: &Element = &*interpolation_coeff; *final_coeff = crypto_algebra::add(final_coeff, &crypto_algebra::mul(interpolation_coeff, eval)); }); - + j = j + 1; }; @@ -391,7 +393,7 @@ module halo2_verifier::shplonk { option::destroy_some(bn254_utils::deserialize_fr(&x"0900000000000000000000000000000000000000000000000000000000000000")), ) ]; - + let (rotation_sets, super_point_set) = construct_intermediate_sets(&queries); assert!(vector::length(&super_point_set) == vector::length(&queries), 100); // Used same g1 elements diff --git a/packages/verifier/sources/shuffle.move b/packages/verifier/sources/shuffle.move new file mode 100644 index 0000000..8f459bd --- /dev/null +++ b/packages/verifier/sources/shuffle.move @@ -0,0 +1,139 @@ +module halo2_verifier::shuffle { + use std::vector::{Self, for_each_ref}; + use aptos_std::crypto_algebra::{Self, Element}; + use aptos_std::bn254_algebra::{G1, Fr}; + + use halo2_common::domain::{Self, Domain}; + use halo2_common::expression::{Self, Expression}; + use halo2_common::query::{Self, VerifierQuery}; + use halo2_common::i32; + use halo2_verifier::protocol::{Self, Protocol, Shuffle}; + use halo2_verifier::transcript::{Self, Transcript}; + + struct Commited has copy, drop { + product_commitment: Element, + } + + struct Evaluated has drop { + commited: Commited, + product_eval: Element, + product_next_eval: Element, + } + + public fun shuffles_read_product_commitments(transcript: &mut Transcript): Commited { + Commited { + product_commitment: transcript::read_point(transcript), + } + } + + public fun evaluate(c: &Commited, transcript: &mut Transcript): Evaluated { + let product_eval = transcript::read_scalar(transcript); + let product_next_eval = transcript::read_scalar(transcript); + Evaluated { + commited: *c, + product_eval, + product_next_eval, + } + } + + public fun expression( + self: &Evaluated, + shuffle: &Shuffle, + coeff_pool: &vector>, + advice_evals: &vector>, + fixed_evals: &vector>, + instance_evals: &vector>, + challenges: &vector>, + l_0: &Element, + l_last: &Element, + l_blind: &Element, + theta: &Element, + gamma: &Element, + result: &mut vector>, + ) { + let active_rows = crypto_algebra::sub(&crypto_algebra::one(), &crypto_algebra::add(l_last, l_blind)); + + // z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma) + let product_expression = { + let left = crypto_algebra::mul( + &self.product_next_eval, + &crypto_algebra::add( + &compress_expressions( + protocol::shuffle_exprs(shuffle), + coeff_pool, + advice_evals, + fixed_evals, + instance_evals, + challenges, + theta + ), gamma, + ), + ); + let right = crypto_algebra::mul( + &self.product_eval, + &crypto_algebra::add( + &compress_expressions( + protocol::shuffle_input_exprs(shuffle), + coeff_pool, + advice_evals, + fixed_evals, + instance_evals, + challenges, + theta + ), gamma, + ), + ); + crypto_algebra::mul(&active_rows, &crypto_algebra::sub(&left, &right)) + }; + + // l_0(X) * (1 - z'(X)) = 0 + vector::push_back(result, crypto_algebra::mul(l_0, &crypto_algebra::sub(&crypto_algebra::one(), &self.product_eval))); + // l_last(X) * (z(X)^2 - z(X)) = 0 + vector::push_back( + result, + crypto_algebra::mul(l_last, &crypto_algebra::sub(&crypto_algebra::sqr(&self.product_eval), &self.product_eval)) + ); + // (1 - (l_last(X) + l_blind(X))) * ( z(\omega X) (s(X) + \gamma) - z(X) (a(X) + \gamma)) + vector::push_back(result, product_expression); + } + + fun compress_expressions(exprs: &vector, + coeff_pool: &vector>, + advice_evals: &vector>, + fixed_evals: &vector>, + instance_evals: &vector>, + challenges: &vector>, + theta: &Element + ): Element { + let acc = crypto_algebra::zero(); + let i = 0; + let len = vector::length(exprs); + while (i < len) { + let eval = expression::evaluate( + vector::borrow(exprs, i), + coeff_pool, + advice_evals, + fixed_evals, + instance_evals, + challenges + ); + acc = crypto_algebra::add(&crypto_algebra::mul(theta, &acc), &eval); + i = i + 1; + }; + acc + } + + public fun queries(e: &vector, queries: &mut vector, _protocol: &Protocol, domain: &Domain, x: &Element) { + let x_next = domain::rotate_omega(domain, x, &i32::from(1)); + for_each_ref(e, |evaluated| { + let eval: &Evaluated = evaluated; + // Open shuffle product commitment at x + vector::push_back(queries, query::new_commitment(eval.commited.product_commitment, *x, eval.product_eval)); + // Open shuffle product commitment at \omega x + vector::push_back( + queries, + query::new_commitment(eval.commited.product_commitment, x_next, eval.product_next_eval) + ); + }); + } +} diff --git a/packages/verifier/tests/verifier_test.move b/packages/verifier/tests/verifier_test.move index 5e69b5a..f1dc4bb 100644 --- a/packages/verifier/tests/verifier_test.move +++ b/packages/verifier/tests/verifier_test.move @@ -64,6 +64,8 @@ module halo2_verifier::verifier_test { x"0200000000000200000002000000010000000300000001000000010003000000000000000100000001000000010000000300000001000000" ], vector[], + vector[], + vector[], vector[] ); let proof_gwc = x"06661ff6e3bd260e8dc4f9096034eaf96782d3fed55ec7fbc5928d2a8de1dd5ce1563c6c4fbc544a0544f957202b6cdcfb0b2e203ba886694a19542d8f749c2d9d0fe09c16f5172e45e48f82f28679649c482ae4baacec0c4d5514711eb4aa255508673b9a378da619ab1d60b24a077e7d4489f19e8b6610542d92ff65bb8d6b5442ab04b452a5199776c980dafb6e6ef641b08ce72c0c1560256b552a2a0520ce48ece7c5dd11faa9945ef0cdaffe033d613b6ee01a9584338a37dc189b6954e0fb075b65df7afa31f16a23fdda32d53accf86e4ab33c64b9237f9c7717326762daa7c924e89622b9a2890ea0156ca39d9c628a8ed55f375fe8f4b68abdb6088af71dd45d783231b4faa779783b65a84835c6c236519f0a38939b5c0228b768bc5d4668208d046fdfcaf5413bcfa53f74de2a61f1ed1fe8431c2737c014080db79cc9cd424cd8131078529850c0ded21a5807d05db2fad9c23474aea774b111911545da9b75a4a67812e017f1347cf6a3f9271c9eec4df98e8579de13d4cc2d4f3c08b8b45a32663c3901e42925168458ebfbf3b6581643e5e04608c0d3471dca54cb9be51eb4e25f06d57c249a26621cedd680079d68de4abdec84fd1a3509776e1e09708970ab57c5106bee4e5e0e51ebd795e8e1a66ff1d9e5e7f2a7591573953d6badc461d33b91975bab8d430f38a373514d6a1366509fe7ae55d0672784920c2053a92f465f0dbc41d5adecf1840ef2c8f67415112d7453ddaaf3631b2207f8f3a917d3e1de062ff184fdd8684327433dd93e3639fdc28b00a5a0772948248a8754af35fc7df9890f9d5bdde4c2396a28362a60fdb51166c9b860332685c17d9e580ee3485756d286a32a6cd81364ab1b49929264856d06392aaa900e44f14f961f1f14e35de6cbf690f445166ce56877c776896e9b94d3f760cfd011de03e0387f359c7ccc9de6ba71a8bb4f0116316aa00e56a04f792d56853add02d7a0c03ff8b5ced61ce8e1792ef035cf39fea4c322a5dd78de3bdc18d5974f06a064d7f5899766ecf2ec5482a52c17cf732e80a615c4498d830527f22eef9a2c990a63d4895d9801a973f512ace8ea66236fcfbe1f02acd7277f22d0a9e6652e0850bd3e164ebb21da674035a210b021735dccb853ea83e6d59bb69284b773043a3d7748334b2c42fcf3f145577aa7ce128403106327b6deee886aed599c7c2068346f4af3a805ec971c6c323d87fe2e2610f615c0ebfa03b516c0b796ad680a3f929d626e5279ac35d1d357911f1c286e796af82eaca91e4c02a78fc6d17a2402339241206bc9b930ecca47d1ced7fb5712192b9b0b60feb69c407d6e3f672985dba3a606ce06bb728e388c39f288350868dbee75f1c8a4f4ef0418eb01074b549dcc9c88c83156da1c9431d6d0c6de731302910e16c9b677b56b7f6e9a0e18a8f7f4a77a6a4064c5881af52c93e0a32de9f9d64ad39936fb85207dafb5b94f";