From de1326d837ac2fd97363793e30d0639a95bdc0e9 Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Sun, 19 Nov 2023 14:23:24 +0300 Subject: [PATCH 01/65] feat: chunked lookup verifier (#1) --- Cargo.toml | 8 +-- src/codegen/evaluator.rs | 149 ++++++++++++++++++++++----------------- src/codegen/pcs.rs | 14 ++-- src/codegen/util.rs | 55 +++++++-------- src/test.rs | 18 +++++ 5 files changed, 140 insertions(+), 104 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8106040..b39dde2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2", tag = "v2023_04_20" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", ref= "3d7b5e61b3052680ccb279e05bdcc21dd8a8fedf" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" -ruint = "1" +ruint = "1.8.0" sha3 = "0.10" itertools = "0.11.0" @@ -20,7 +20,7 @@ revm = { version = "3.3.0", optional = true } [dev-dependencies] rand = "0.8.5" revm = "3.3.0" -halo2_maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "maingate" } +halo2_maingate = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "maingate" } [features] default = [] @@ -28,4 +28,4 @@ evm = ["dep:revm"] [[example]] name = "separately" -required-features = ["evm"] +required-features = ["evm"] \ No newline at end of file diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index c65a654..b22e8d6 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -121,75 +121,110 @@ where } pub fn lookup_computations(&self) -> Vec<(Vec, String)> { - let input_tables = self + let evaluate = |expressions: &Vec<_>| { + let (lines, inputs) = expressions + .iter() + .map(|expression| self.evaluate(expression)) + .fold((Vec::new(), Vec::new()), |mut acc, result| { + acc.0.extend(result.0); + acc.1.push(result.1); + acc + }); + self.reset(); + (lines, inputs) + }; + let inputs_tables = self .cs .lookups() .iter() .map(|lookup| { - let [(input_lines, inputs), (table_lines, tables)] = - [lookup.input_expressions(), lookup.table_expressions()].map(|expressions| { - let (lines, inputs) = expressions - .iter() - .map(|expression| self.evaluate(expression)) - .fold((Vec::new(), Vec::new()), |mut acc, result| { - acc.0.extend(result.0); - acc.1.push(result.1); - acc - }); - self.reset(); - (lines, inputs) - }); - (input_lines, inputs, table_lines, tables) + let inputs = lookup + .input_expressions() + .iter() + .map(evaluate) + .collect_vec(); + let table = evaluate(lookup.table_expressions()); + (inputs, table) }) .collect_vec(); - izip!(input_tables, &self.data.lookup_evals) - .flat_map(|(input_table, evals)| { - let (input_lines, inputs, table_lines, tables) = input_table; - let (input_0, rest_inputs) = inputs.split_first().unwrap(); + izip!(inputs_tables, &self.data.lookup_evals) + .flat_map(|(inputs_tables, evals)| { + let (inputs, (table_lines, tables)) = inputs_tables; + let num_inputs = inputs.len(); let (table_0, rest_tables) = tables.split_first().unwrap(); - let (z, z_next, p_input, p_input_prev, p_table) = evals; + let (phi, phi_next, m) = evals; [ vec![ format!("let l_0 := mload(L_0_MPTR)"), - format!("let eval := addmod(l_0, mulmod(l_0, sub(r, {z}), r), r)"), + format!("let eval := mulmod(l_0, {phi}, r)"), + ], + vec![ + format!("let l_last := mload(L_LAST_MPTR)"), + format!("let eval := mulmod(l_last, {phi}, r)"), ], - { - let item = format!("addmod(mulmod({z}, {z}, r), sub(r, {z}), r)"); - vec![ - format!("let l_last := mload(L_LAST_MPTR)"), - format!("let eval := mulmod(l_last, {item}, r)"), - ] - }, chain![ - ["let theta := mload(THETA_MPTR)", "let input"].map(str::to_string), - code_block::<1, false>(chain![ - input_lines, - [format!("input := {input_0}")], - rest_inputs.iter().map(|input| format!( - "input := addmod(mulmod(input, theta, r), {input}, r)" - )) - ]), - ["let table"].map(str::to_string), + [ + "let theta := mload(THETA_MPTR)", + "let beta := mload(BETA_MPTR)", + "let table" + ] + .map(str::to_string), code_block::<1, false>(chain![ table_lines, [format!("table := {table_0}")], rest_tables.iter().map(|table| format!( "table := addmod(mulmod(table, theta, r), {table}, r)" - )) + )), + [format!("table := addmod(table, beta, r)")], ]), - { - let lhs = format!("addmod({p_input}, beta, r)"); - let rhs = format!("addmod({p_table}, gamma, r)"); - let permuted = format!("mulmod({lhs}, {rhs}, r)"); - let input = - "mulmod(addmod(input, beta, r), addmod(table, gamma, r), r)"; - [ - format!("let beta := mload(BETA_MPTR)"), - format!("let gamma := mload(GAMMA_MPTR)"), - format!("let lhs := mulmod({z_next}, {permuted}, r)"), - format!("let rhs := mulmod({z}, {input}, r)"), + izip!(0.., inputs.into_iter()).flat_map(|(idx, (input_lines, inputs))| { + let (input_0, rest_inputs) = inputs.split_first().unwrap(); + let ident = format!("input_{idx}"); + chain![ + [format!("let {ident}")], + code_block::<1, false>(chain![ + input_lines, + [format!("{ident} := {input_0}")], + rest_inputs.iter().map(|input| format!( + "{ident} := addmod(mulmod({ident}, theta, r), {input}, r)" + )), + [format!("{ident} := addmod({ident}, beta, r)")], + ]), ] - }, + }), + [format!("let lhs"), format!("let rhs")], + (0..num_inputs).flat_map(|i| { + assert_ne!(num_inputs, 0); + if num_inputs == 1 { + vec![format!("rhs := table")] + } else { + let idents = (0..num_inputs) + .filter(|j| *j != i) + .map(|idx| format!("input_{idx}")) + .collect_vec(); + let (ident_0, rest_idents) = idents.split_first().unwrap(); + code_block::<1, false>(chain![ + [format!("let tmp := {ident_0}")], + chain![rest_idents] + .map(|ident| format!("tmp := mulmod(tmp, {ident}, r)")), + [format!("rhs := addmod(rhs, tmp, r)"),], + (i == num_inputs - 1) + .then(|| format!("rhs := mulmod(rhs, table, r)")), + ]) + } + }), + code_block::<1, false>(chain![ + [format!("let tmp := input_0")], + (1..num_inputs) + .map(|idx| format!("tmp := mulmod(tmp, input_{idx}, r)")), + [ + format!("rhs := addmod(rhs, sub(r, mulmod({m}, tmp, r)), r)"), + { + let item = format!("addmod({phi_next}, sub(r, {phi}), r)"); + format!("lhs := mulmod(mulmod(table, tmp, r), {item}, r)") + }, + ], + ]), { let l_inactive = "addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), r)"; let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); @@ -199,20 +234,6 @@ where }, ] .collect_vec(), - { - let l_0 = "mload(L_0_MPTR)"; - let item = format!("addmod({p_input}, sub(r, {p_table}), r)"); - vec![format!("let eval := mulmod({l_0}, {item}, r)")] - }, - { - let l_inactive = "addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), r)"; - let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); - let lhs = format!("addmod({p_input}, sub(r, {p_table}), r)"); - let rhs = format!("addmod({p_input}, sub(r, {p_input_prev}), r)"); - vec![format!( - "let eval := mulmod({l_active}, mulmod({lhs}, {rhs}, r), r)" - )] - }, ] }) .zip(iter::repeat("eval".to_string())) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 2b7536a..8b814db 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -47,17 +47,15 @@ pub(crate) fn queries(meta: &ConstraintSystemMeta, data: &Data) -> Vec { .skip(1) .map(|(&comm, evals)| Query::new(comm, meta.rotation_last, evals.2)), izip!( - &data.lookup_permuted_comms, - &data.lookup_z_comms, + &data.lookup_m_comms, + &data.lookup_phi_comms, &data.lookup_evals ) - .flat_map(|(permuted_comms, &z_comm, evals)| { + .flat_map(|(&m_comm, &phi_comm, evals)| { [ - Query::new(z_comm, 0, evals.0), - Query::new(permuted_comms.0, 0, evals.2), - Query::new(permuted_comms.1, 0, evals.4), - Query::new(permuted_comms.0, -1, evals.3), - Query::new(z_comm, 1, evals.1), + Query::new(phi_comm, 0, evals.0), + Query::new(phi_comm, 1, evals.1), + Query::new(m_comm, 0, evals.2), ] }), meta.fixed_queries.iter().map(|query| { diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 9d3aea9..ec90f0c 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -20,9 +20,9 @@ pub(crate) struct ConstraintSystemMeta { pub(crate) num_fixeds: usize, pub(crate) permutation_columns: Vec>, pub(crate) permutation_chunk_len: usize, - pub(crate) num_lookup_permuteds: usize, + pub(crate) num_lookup_ms: usize, pub(crate) num_permutation_zs: usize, - pub(crate) num_lookup_zs: usize, + pub(crate) num_lookup_phis: usize, pub(crate) num_quotients: usize, pub(crate) advice_queries: Vec<(usize, i32)>, pub(crate) fixed_queries: Vec<(usize, i32)>, @@ -39,13 +39,13 @@ impl ConstraintSystemMeta { let num_fixeds = cs.num_fixed_columns(); let permutation_columns = cs.permutation().get_columns(); let permutation_chunk_len = cs.degree() - 2; - let num_lookup_permuteds = 2 * cs.lookups().len(); + let num_lookup_ms = cs.lookups().len(); let num_permutation_zs = cs .permutation() .get_columns() .chunks(cs.degree() - 2) .count(); - let num_lookup_zs = cs.lookups().len(); + let num_lookup_phis = cs.lookups().len(); let num_quotients = cs.degree() - 1; let advice_queries = cs .advice_queries() @@ -62,7 +62,7 @@ impl ConstraintSystemMeta { + 1 + cs.permutation().get_columns().len() + (3 * num_permutation_zs - 1) - + 5 * cs.lookups().len(); + + 3 * cs.lookups().len(); let num_phase = *cs.advice_column_phase().iter().max().unwrap_or(&0) as usize + 1; // Indices of advice and challenge are not same as their position in calldata/memory, // because we support multiple phases, we need to remap them and find their actual indices. @@ -95,9 +95,9 @@ impl ConstraintSystemMeta { num_fixeds, permutation_columns, permutation_chunk_len, - num_lookup_permuteds, + num_lookup_ms, num_permutation_zs, - num_lookup_zs, + num_lookup_phis, num_quotients, advice_queries, fixed_queries, @@ -113,10 +113,10 @@ impl ConstraintSystemMeta { pub(crate) fn num_advices(&self) -> Vec { chain![ self.num_user_advices.iter().cloned(), - (self.num_lookup_permuteds != 0).then_some(self.num_lookup_permuteds), // lookup permuted + (self.num_lookup_ms != 0).then_some(self.num_lookup_ms), // lookup permuted [ - self.num_permutation_zs + self.num_lookup_zs + 1, // permutation and lookup grand products, random - self.num_quotients, // quotients + self.num_permutation_zs + self.num_lookup_phis + 1, // permutation and lookup grand products, random + self.num_quotients, // quotients ], ] .collect() @@ -127,7 +127,7 @@ impl ConstraintSystemMeta { // If there is no lookup used, merge also beta and gamma into the last user phase, to avoid // squeezing challenge from nothing. // Otherwise, merge theta into last user phase since they are originally adjacent. - if self.num_lookup_permuteds == 0 { + if self.num_lookup_ms == 0 { *num_challenges.last_mut().unwrap() += 3; // theta, beta, gamma num_challenges.extend([ 1, // y @@ -149,7 +149,7 @@ impl ConstraintSystemMeta { } pub(crate) fn num_lookups(&self) -> usize { - self.num_lookup_zs + self.num_lookup_phis } pub(crate) fn proof_len(&self, scheme: BatchOpenScheme) -> usize { @@ -179,9 +179,9 @@ pub(crate) struct Data { pub(crate) fixed_comms: Vec, pub(crate) permutation_comms: HashMap, EcPoint>, pub(crate) advice_comms: Vec, - pub(crate) lookup_permuted_comms: Vec<(EcPoint, EcPoint)>, + pub(crate) lookup_m_comms: Vec, pub(crate) permutation_z_comms: Vec, - pub(crate) lookup_z_comms: Vec, + pub(crate) lookup_phi_comms: Vec, pub(crate) random_comm: EcPoint, pub(crate) challenges: Vec, @@ -192,7 +192,7 @@ pub(crate) struct Data { pub(crate) random_eval: Word, pub(crate) permutation_evals: HashMap, Word>, pub(crate) permutation_z_evals: Vec<(Word, Word, Word)>, - pub(crate) lookup_evals: Vec<(Word, Word, Word, Word, Word)>, + pub(crate) lookup_evals: Vec<(Word, Word, Word)>, pub(crate) computed_quotient_comm: EcPoint, pub(crate) computed_quotient_eval: Word, @@ -211,10 +211,10 @@ impl Data { let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; - let lookup_permuted_comm_start = advice_comm_start + 2 * meta.advice_indices.len(); - let permutation_z_comm_start = lookup_permuted_comm_start + 2 * meta.num_lookup_permuteds; - let lookup_z_comm_start = permutation_z_comm_start + 2 * meta.num_permutation_zs; - let random_comm_start = lookup_z_comm_start + 2 * meta.num_lookup_zs; + let lookup_m_comm_start = advice_comm_start + 2 * meta.advice_indices.len(); + let permutation_z_comm_start = lookup_m_comm_start + 2 * meta.num_lookup_ms; + let lookup_phi_comm_start = permutation_z_comm_start + 2 * meta.num_permutation_zs; + let random_comm_start = lookup_phi_comm_start + 2 * meta.num_lookup_phis; let quotient_comm_start = random_comm_start + 2; let eval_cptr = quotient_comm_start + 2 * meta.num_quotients; @@ -224,7 +224,7 @@ impl Data { let permutation_eval_cptr = random_eval_cptr + 1; let permutation_z_eval_cptr = permutation_eval_cptr + meta.num_permutations(); let lookup_eval_cptr = permutation_z_eval_cptr + 3 * meta.num_permutation_zs - 1; - let w_cptr = lookup_eval_cptr + 5 * meta.num_lookups(); + let w_cptr = lookup_eval_cptr + 3 * meta.num_lookups(); let fixed_comms = EcPoint::range(fixed_comm_mptr) .take(meta.num_fixeds) @@ -240,15 +240,14 @@ impl Data { .map(|idx| advice_comm_start + 2 * idx) .map_into() .collect(); - let lookup_permuted_comms = EcPoint::range(lookup_permuted_comm_start) - .take(meta.num_lookup_permuteds) - .tuples() + let lookup_m_comms = EcPoint::range(lookup_m_comm_start) + .take(meta.num_lookup_ms) .collect(); let permutation_z_comms = EcPoint::range(permutation_z_comm_start) .take(meta.num_permutation_zs) .collect(); - let lookup_z_comms = EcPoint::range(lookup_z_comm_start) - .take(meta.num_lookup_zs) + let lookup_phi_comms = EcPoint::range(lookup_phi_comm_start) + .take(meta.num_lookup_phis) .collect(); let random_comm = random_comm_start.into(); let computed_quotient_comm = EcPoint::new( @@ -284,7 +283,7 @@ impl Data { .tuples() .collect_vec(); let lookup_evals = Word::range(lookup_eval_cptr) - .take(5 * meta.num_lookup_zs) + .take(3 * meta.num_lookup_phis) .tuples() .collect_vec(); let computed_quotient_eval = Ptr::memory("QUOTIENT_EVAL_MPTR").into(); @@ -298,9 +297,9 @@ impl Data { fixed_comms, permutation_comms, advice_comms, - lookup_permuted_comms, + lookup_m_comms, permutation_z_comms, - lookup_z_comms, + lookup_phi_comms, random_comm, computed_quotient_comm, diff --git a/src/test.rs b/src/test.rs index fb31148..ee491b8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -343,6 +343,8 @@ mod halo2 { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { + meta.set_minimum_degree(9); + let selectors = [(); 10].map(|_| meta.selector()); let complex_selectors = [(); 10].map(|_| meta.complex_selector()); let fixeds = [(); 10].map(|_| meta.fixed_column()); @@ -405,6 +407,22 @@ mod halo2 { }); } + for _ in 0..10 { + meta.lookup_any("", |meta| { + let (q1, q2, q3) = complex_selectors.iter().tuple_windows().next().unwrap(); + let (f1, f2, f3) = fixeds.iter().tuple_windows().next().unwrap(); + let (a1, a2, a3) = advices.iter().tuple_windows().next().unwrap(); + izip!([q1, q2, q3], [f1, f2, f3], [a1, a2, a3]) + .map(|(q, f, a)| { + let q = meta.query_selector(*q); + let f = meta.query_fixed(*f, Rotation::cur()); + let a = meta.query_advice(*a, Rotation::cur()); + (q * a, f) + }) + .collect_vec() + }); + } + fixeds.map(|column| meta.enable_equality(column)); advices.map(|column| meta.enable_equality(column)); meta.enable_equality(instance); From 1f7315b7886b9a4bb0800db4375b914ecccf37e7 Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:38:45 +0000 Subject: [PATCH 02/65] feat: lookup modularity (#2) --- .github/workflows/ci.yml | 2 +- Cargo.toml | 8 +- README.md | 17 +- examples/separately.rs | 2 +- src/codegen.rs | 25 +-- src/codegen/evaluator.rs | 101 ++++++++++++ src/codegen/pcs.rs | 48 ++++++ src/codegen/template.rs | 2 +- src/codegen/util.rs | 311 ++++++++++++++++++++++++++++++++++++ src/test.rs | 8 +- templates/Halo2Verifier.sol | 62 ++++--- 11 files changed, 537 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03e508c..5146421 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: cache-on-failure: true - name: Install solc - run: (hash svm 2>/dev/null || cargo install --locked --git https://github.com/alloy-rs/svm-rs) && svm install 0.8.21 && solc --version + run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.21 && solc --version - name: Run test run: cargo test --workspace --all-features --all-targets -- --nocapture diff --git a/Cargo.toml b/Cargo.toml index b39dde2..5aca2e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", ref= "3d7b5e61b3052680ccb279e05bdcc21dd8a8fedf" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", branch="main" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" @@ -15,16 +15,18 @@ itertools = "0.11.0" blake2b_simd = "1" # For feature = "evm" -revm = { version = "3.3.0", optional = true } +revm = { version = "3.3.0", default-features = false, optional = true } [dev-dependencies] rand = "0.8.5" -revm = "3.3.0" +revm = { version = "3.3.0", default-features = false } halo2_maingate = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "maingate" } + [features] default = [] evm = ["dep:revm"] +mv-lookup = ["halo2_proofs/mv-lookup", "halo2_maingate/mv-lookup"] [[example]] name = "separately" diff --git a/README.md b/README.md index 5152dfd..7cae107 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ > ⚠️ This repo has NOT been audited and is NOT intended for a production environment yet. -Solidity verifier generator for [`halo2`](http://github.com/privacy-scaling-explorations/halo2) proof with KZG polynomial commitment scheme on BN254 +Solidity verifier generator for [`halo2`](http://github.com/privacy-scaling-explorations/halo2) proof with KZG polynomial commitment scheme on BN254. + +For audited solidity verifier generator and proof aggregation toolkits, please refer to [`snark-verifier`](http://github.com/axiom-crypto/snark-verifier). ## Usage @@ -30,16 +32,25 @@ let calldata = encode_calldata(vk_address, &proof, &instances); Note that function selector is already included. -## Limitations +## Limitations & Caveats - It only allows circuit with **exact 1 instance column** and **no rotated query to this instance column**. -- Currently even the `configure` is same, the [selector compression](https://github.com/privacy-scaling-explorations/halo2/blob/7a2165617195d8baa422ca7b2b364cef02380390/halo2_proofs/src/plonk/circuit/compress_selectors.rs#L51) might lead to different configuration when selector assignments are different. After PR https://github.com/privacy-scaling-explorations/halo2/pull/212 is merged we will have an alternative API to do key generation without selector compression. +- Currently even the `configure` is same, the [selector compression](https://github.com/privacy-scaling-explorations/halo2/blob/7a2165617195d8baa422ca7b2b364cef02380390/halo2_proofs/src/plonk/circuit/compress_selectors.rs#L51) might lead to different configuration when selector assignments are different. To avoid this, please use [`keygen_vk_custom`](https://github.com/privacy-scaling-explorations/halo2/blob/6fc6d7ca018f3899b030618cb18580249b1e7c82/halo2_proofs/src/plonk/keygen.rs#L223) with `compress_selectors: false` to do key generation without selector compression. - Now it only supports BDFG21 batch open scheme (aka SHPLONK), GWC19 is not yet implemented. ## Compatibility The [`Keccak256Transcript`](./src/transcript.rs#L19) behaves exactly same as the `EvmTranscript` in `snark-verifier`. +## Design Rationale + +The current solidity verifier generator within `snark-verifier` faces a couple of issues: + +- The generator receives only unoptimized, low-level operations, such as add or mul. As a result, it currently unrolls all assembly codes, making it susceptible to exceeding the contract size limit, even with a moderately sized circuit. +- The existing solution involves complex abstractions and APIs for consumers. + +This repository is a ground-up rebuild, addressing these concerns while maintaining a focus on code size and readability. Remarkably, the gas cost is comparable, if not slightly lower, than the one generated by `snark-verifier`. + ## Acknowledgement The template is heavily inspired by Aztec's [`BaseUltraVerifier.sol`](https://github.com/AztecProtocol/barretenberg/blob/4c456a2b196282160fd69bead6a1cea85289af37/sol/src/ultra/BaseUltraVerifier.sol). diff --git a/examples/separately.rs b/examples/separately.rs index d748332..b2e58c5 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -223,7 +223,7 @@ mod prelude { ff::PrimeField, }, plonk::*, - poly::{commitment::Params, kzg::commitment::ParamsKZG, Rotation}, + poly::{kzg::commitment::ParamsKZG, Rotation}, }; pub use rand::{ rngs::{OsRng, StdRng}, diff --git a/src/codegen.rs b/src/codegen.rs index 547a90a..d950c27 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -167,6 +167,7 @@ impl<'a> SolidityGenerator<'a> { let constants = { let domain = self.vk.get_domain(); let vk_digest = fr_to_u256(vk_transcript_repr(self.vk)); + let num_instances = U256::from(self.num_instances); let k = U256::from(domain.k()); let n_inv = fr_to_u256(bn256::Fr::from(1 << domain.k()).invert().unwrap()); let omega = fr_to_u256(domain.get_omega()); @@ -175,8 +176,7 @@ impl<'a> SolidityGenerator<'a> { let l = self.meta.rotation_last.unsigned_abs() as u64; fr_to_u256(domain.get_omega_inv().pow_vartime([l])) }; - let num_instances = U256::from(self.num_instances); - let has_accumulator = U256::from(self.acc_encoding.is_some()); + let has_accumulator = U256::from(self.acc_encoding.is_some() as usize); let acc_offset = self .acc_encoding .map(|acc_encoding| U256::from(acc_encoding.offset)) @@ -195,12 +195,12 @@ impl<'a> SolidityGenerator<'a> { let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); vec![ ("vk_digest", vk_digest), + ("num_instances", num_instances), ("k", k), ("n_inv", n_inv), ("omega", omega), ("omega_inv", omega_inv), ("omega_inv_to_l", omega_inv_to_l), - ("num_instances", num_instances), ("has_accumulator", has_accumulator), ("acc_offset", acc_offset), ("num_acc_limbs", num_acc_limbs), @@ -267,7 +267,7 @@ impl<'a> SolidityGenerator<'a> { Halo2Verifier { scheme: self.scheme, - vk: (!separate).then_some(vk), + embedded_vk: (!separate).then_some(vk), vk_len, vk_mptr, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, @@ -301,15 +301,18 @@ impl<'a> SolidityGenerator<'a> { Gwc19 => unimplemented!(), }; - itertools::max(chain![ - // Hashing advice commitments - chain![self.meta.num_advices().into_iter()].map(|n| n * 2 + 1), - // Hashing evaluations - [self.meta.num_evals + 1], + itertools::max([ + // Keccak256 input (can overwrite vk) + itertools::max(chain![ + self.meta.num_advices().into_iter().map(|n| n * 2 + 1), + [self.meta.num_evals + 1], + ]) + .unwrap() + .saturating_sub(vk.len() / 0x20), // PCS computation - [pcs_computation], + pcs_computation, // Pairing - [12], + 12, ]) .unwrap() * 0x20 diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index b22e8d6..39428b9 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -120,6 +120,7 @@ where .collect() } + #[cfg(feature = "mv-lookup")] pub fn lookup_computations(&self) -> Vec<(Vec, String)> { let evaluate = |expressions: &Vec<_>| { let (lines, inputs) = expressions @@ -240,6 +241,106 @@ where .collect_vec() } + #[cfg(not(feature = "mv-lookup"))] + pub fn lookup_computations(&self) -> Vec<(Vec, String)> { + let input_tables = self + .cs + .lookups() + .iter() + .map(|lookup| { + let [(input_lines, inputs), (table_lines, tables)] = + [lookup.input_expressions(), lookup.table_expressions()].map(|expressions| { + let (lines, inputs) = expressions + .iter() + .map(|expression| self.evaluate(expression)) + .fold((Vec::new(), Vec::new()), |mut acc, result| { + acc.0.extend(result.0); + acc.1.push(result.1); + acc + }); + self.reset(); + (lines, inputs) + }); + (input_lines, inputs, table_lines, tables) + }) + .collect_vec(); + izip!(input_tables, &self.data.lookup_evals) + .flat_map(|(input_table, evals)| { + let (input_lines, inputs, table_lines, tables) = input_table; + let (input_0, rest_inputs) = inputs.split_first().unwrap(); + let (table_0, rest_tables) = tables.split_first().unwrap(); + let (z, z_next, p_input, p_input_prev, p_table) = evals; + [ + vec![ + format!("let l_0 := mload(L_0_MPTR)"), + format!("let eval := addmod(l_0, mulmod(l_0, sub(r, {z}), r), r)"), + ], + { + let item = format!("addmod(mulmod({z}, {z}, r), sub(r, {z}), r)"); + vec![ + format!("let l_last := mload(L_LAST_MPTR)"), + format!("let eval := mulmod(l_last, {item}, r)"), + ] + }, + chain![ + ["let theta := mload(THETA_MPTR)", "let input"].map(str::to_string), + code_block::<1, false>(chain![ + input_lines, + [format!("input := {input_0}")], + rest_inputs.iter().map(|input| format!( + "input := addmod(mulmod(input, theta, r), {input}, r)" + )) + ]), + ["let table"].map(str::to_string), + code_block::<1, false>(chain![ + table_lines, + [format!("table := {table_0}")], + rest_tables.iter().map(|table| format!( + "table := addmod(mulmod(table, theta, r), {table}, r)" + )) + ]), + { + let lhs = format!("addmod({p_input}, beta, r)"); + let rhs = format!("addmod({p_table}, gamma, r)"); + let permuted = format!("mulmod({lhs}, {rhs}, r)"); + let input = + "mulmod(addmod(input, beta, r), addmod(table, gamma, r), r)"; + [ + format!("let beta := mload(BETA_MPTR)"), + format!("let gamma := mload(GAMMA_MPTR)"), + format!("let lhs := mulmod({z_next}, {permuted}, r)"), + format!("let rhs := mulmod({z}, {input}, r)"), + ] + }, + { + let l_inactive = "addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), r)"; + let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); + [format!( + "let eval := mulmod({l_active}, addmod(lhs, sub(r, rhs), r), r)" + )] + }, + ] + .collect_vec(), + { + let l_0 = "mload(L_0_MPTR)"; + let item = format!("addmod({p_input}, sub(r, {p_table}), r)"); + vec![format!("let eval := mulmod({l_0}, {item}, r)")] + }, + { + let l_inactive = "addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), r)"; + let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); + let lhs = format!("addmod({p_input}, sub(r, {p_table}), r)"); + let rhs = format!("addmod({p_input}, sub(r, {p_input_prev}), r)"); + vec![format!( + "let eval := mulmod({l_active}, mulmod({lhs}, {rhs}, r), r)" + )] + }, + ] + }) + .zip(iter::repeat("eval".to_string())) + .collect_vec() + } + fn eval(&self, column_type: impl Into, column_index: usize, rotation: i32) -> String { match column_type.into() { Any::Advice(_) => self.data.advice_evals[&(column_index, rotation)].to_string(), diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 8b814db..e00895c 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -32,6 +32,7 @@ impl Query { } } +#[cfg(feature = "mv-lookup")] pub(crate) fn queries(meta: &ConstraintSystemMeta, data: &Data) -> Vec { chain![ meta.advice_queries.iter().map(|query| { @@ -76,6 +77,53 @@ pub(crate) fn queries(meta: &ConstraintSystemMeta, data: &Data) -> Vec { .collect() } +#[cfg(not(feature = "mv-lookup"))] +pub(crate) fn queries(meta: &ConstraintSystemMeta, data: &Data) -> Vec { + chain![ + meta.advice_queries.iter().map(|query| { + let comm = data.advice_comms[query.0]; + let eval = data.advice_evals[query]; + Query::new(comm, query.1, eval) + }), + izip!(&data.permutation_z_comms, &data.permutation_z_evals).flat_map(|(&comm, evals)| { + [Query::new(comm, 0, evals.0), Query::new(comm, 1, evals.1)] + }), + izip!(&data.permutation_z_comms, &data.permutation_z_evals) + .rev() + .skip(1) + .map(|(&comm, evals)| Query::new(comm, meta.rotation_last, evals.2)), + izip!( + &data.lookup_permuted_comms, + &data.lookup_z_comms, + &data.lookup_evals + ) + .flat_map(|(permuted_comms, &z_comm, evals)| { + [ + Query::new(z_comm, 0, evals.0), + Query::new(permuted_comms.0, 0, evals.2), + Query::new(permuted_comms.1, 0, evals.4), + Query::new(permuted_comms.0, -1, evals.3), + Query::new(z_comm, 1, evals.1), + ] + }), + meta.fixed_queries.iter().map(|query| { + let comm = data.fixed_comms[query.0]; + let eval = data.fixed_evals[query]; + Query::new(comm, query.1, eval) + }), + meta.permutation_columns.iter().map(|column| { + let comm = data.permutation_comms[column]; + let eval = data.permutation_evals[column]; + Query::new(comm, 0, eval) + }), + [ + Query::new(data.computed_quotient_comm, 0, data.computed_quotient_eval), + Query::new(data.random_comm, 0, data.random_eval), + ] + ] + .collect() +} + #[derive(Debug)] pub(crate) struct RotationSet { rots: BTreeSet, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index e25f723..55753f9 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -25,7 +25,7 @@ impl Halo2VerifyingKey { #[template(path = "Halo2Verifier.sol")] pub(crate) struct Halo2Verifier { pub(crate) scheme: BatchOpenScheme, - pub(crate) vk: Option, + pub(crate) embedded_vk: Option, pub(crate) vk_len: usize, pub(crate) proof_len: usize, pub(crate) vk_mptr: Ptr, diff --git a/src/codegen/util.rs b/src/codegen/util.rs index ec90f0c..1f31ea7 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -16,6 +16,7 @@ use std::{ }; #[derive(Debug)] +#[cfg(feature = "mv-lookup")] pub(crate) struct ConstraintSystemMeta { pub(crate) num_fixeds: usize, pub(crate) permutation_columns: Vec>, @@ -34,6 +35,27 @@ pub(crate) struct ConstraintSystemMeta { pub(crate) rotation_last: i32, } +#[derive(Debug)] +#[cfg(not(feature = "mv-lookup"))] +pub(crate) struct ConstraintSystemMeta { + pub(crate) num_fixeds: usize, + pub(crate) permutation_columns: Vec>, + pub(crate) permutation_chunk_len: usize, + pub(crate) num_lookup_permuteds: usize, + pub(crate) num_permutation_zs: usize, + pub(crate) num_lookup_zs: usize, + pub(crate) num_quotients: usize, + pub(crate) advice_queries: Vec<(usize, i32)>, + pub(crate) fixed_queries: Vec<(usize, i32)>, + pub(crate) num_evals: usize, + pub(crate) num_user_advices: Vec, + pub(crate) num_user_challenges: Vec, + pub(crate) advice_indices: Vec, + pub(crate) challenge_indices: Vec, + pub(crate) rotation_last: i32, +} + +#[cfg(feature = "mv-lookup")] impl ConstraintSystemMeta { pub(crate) fn new(cs: &ConstraintSystem) -> Self { let num_fixeds = cs.num_fixed_columns(); @@ -168,7 +190,143 @@ impl ConstraintSystemMeta { } } +#[cfg(not(feature = "mv-lookup"))] +impl ConstraintSystemMeta { + pub(crate) fn new(cs: &ConstraintSystem) -> Self { + let num_fixeds = cs.num_fixed_columns(); + let permutation_columns = cs.permutation().get_columns(); + let permutation_chunk_len = cs.degree() - 2; + let num_lookup_permuteds = 2 * cs.lookups().len(); + let num_permutation_zs = cs + .permutation() + .get_columns() + .chunks(cs.degree() - 2) + .count(); + let num_lookup_zs = cs.lookups().len(); + let num_quotients = cs.degree() - 1; + let advice_queries = cs + .advice_queries() + .iter() + .map(|(column, rotation)| (column.index(), rotation.0)) + .collect_vec(); + let fixed_queries = cs + .fixed_queries() + .iter() + .map(|(column, rotation)| (column.index(), rotation.0)) + .collect_vec(); + let num_evals = advice_queries.len() + + fixed_queries.len() + + 1 + + cs.permutation().get_columns().len() + + (3 * num_permutation_zs - 1) + + 5 * cs.lookups().len(); + let num_phase = *cs.advice_column_phase().iter().max().unwrap_or(&0) as usize + 1; + // Indices of advice and challenge are not same as their position in calldata/memory, + // because we support multiple phases, we need to remap them and find their actual indices. + let remapping = |phase: Vec| { + let nums = phase.iter().fold(vec![0; num_phase], |mut nums, phase| { + nums[*phase as usize] += 1; + nums + }); + let offsets = nums + .iter() + .take(num_phase - 1) + .fold(vec![0], |mut offsets, n| { + offsets.push(offsets.last().unwrap() + n); + offsets + }); + let index = phase + .iter() + .scan(offsets, |state, phase| { + let index = state[*phase as usize]; + state[*phase as usize] += 1; + Some(index) + }) + .collect::>(); + (nums, index) + }; + let (num_user_advices, advice_indices) = remapping(cs.advice_column_phase()); + let (num_user_challenges, challenge_indices) = remapping(cs.challenge_phase()); + let rotation_last = -(cs.blinding_factors() as i32 + 1); + Self { + num_fixeds, + permutation_columns, + permutation_chunk_len, + num_lookup_permuteds, + num_permutation_zs, + num_lookup_zs, + num_quotients, + advice_queries, + fixed_queries, + num_evals, + num_user_advices, + num_user_challenges, + advice_indices, + challenge_indices, + rotation_last, + } + } + + pub(crate) fn num_advices(&self) -> Vec { + chain![ + self.num_user_advices.iter().cloned(), + (self.num_lookup_permuteds != 0).then_some(self.num_lookup_permuteds), // lookup permuted + [ + self.num_permutation_zs + self.num_lookup_zs + 1, // permutation and lookup grand products, random + self.num_quotients, // quotients + ], + ] + .collect() + } + + pub(crate) fn num_challenges(&self) -> Vec { + let mut num_challenges = self.num_user_challenges.clone(); + // If there is no lookup used, merge also beta and gamma into the last user phase, to avoid + // squeezing challenge from nothing. + // Otherwise, merge theta into last user phase since they are originally adjacent. + if self.num_lookup_permuteds == 0 { + *num_challenges.last_mut().unwrap() += 3; // theta, beta, gamma + num_challenges.extend([ + 1, // y + 1, // x + ]); + } else { + *num_challenges.last_mut().unwrap() += 1; // theta + num_challenges.extend([ + 2, // beta, gamma + 1, // y + 1, // x + ]); + } + num_challenges + } + + pub(crate) fn num_permutations(&self) -> usize { + self.permutation_columns.len() + } + + pub(crate) fn num_lookups(&self) -> usize { + self.num_lookup_zs + } + + pub(crate) fn proof_len(&self, scheme: BatchOpenScheme) -> usize { + self.num_advices().iter().sum::() * 0x40 + + self.num_evals * 0x20 + + self.batch_open_proof_len(scheme) + } + + pub(crate) fn batch_open_proof_len(&self, scheme: BatchOpenScheme) -> usize { + match scheme { + Bdfg21 => 2 * 0x40, + Gwc19 => { + unimplemented!() + } + } + } +} + #[derive(Debug)] +#[cfg(feature = "mv-lookup")] pub(crate) struct Data { pub(crate) challenge_mptr: Ptr, pub(crate) theta_mptr: Ptr, @@ -198,6 +356,38 @@ pub(crate) struct Data { pub(crate) computed_quotient_eval: Word, } +#[cfg(not(feature = "mv-lookup"))] +#[derive(Debug)] +pub(crate) struct Data { + pub(crate) challenge_mptr: Ptr, + pub(crate) theta_mptr: Ptr, + + pub(crate) quotient_comm_cptr: Ptr, + pub(crate) w_cptr: Ptr, + + pub(crate) fixed_comms: Vec, + pub(crate) permutation_comms: HashMap, EcPoint>, + pub(crate) advice_comms: Vec, + pub(crate) lookup_permuted_comms: Vec<(EcPoint, EcPoint)>, + pub(crate) permutation_z_comms: Vec, + pub(crate) lookup_z_comms: Vec, + pub(crate) random_comm: EcPoint, + + pub(crate) challenges: Vec, + + pub(crate) instance_eval: Word, + pub(crate) advice_evals: HashMap<(usize, i32), Word>, + pub(crate) fixed_evals: HashMap<(usize, i32), Word>, + pub(crate) random_eval: Word, + pub(crate) permutation_evals: HashMap, Word>, + pub(crate) permutation_z_evals: Vec<(Word, Word, Word)>, + pub(crate) lookup_evals: Vec<(Word, Word, Word, Word, Word)>, + + pub(crate) computed_quotient_comm: EcPoint, + pub(crate) computed_quotient_eval: Word, +} + +#[cfg(feature = "mv-lookup")] impl Data { pub(crate) fn new( meta: &ConstraintSystemMeta, @@ -317,6 +507,127 @@ impl Data { } } +#[cfg(not(feature = "mv-lookup"))] +impl Data { + pub(crate) fn new( + meta: &ConstraintSystemMeta, + vk: &Halo2VerifyingKey, + vk_mptr: Ptr, + proof_cptr: Ptr, + ) -> Self { + let fixed_comm_mptr = vk_mptr + vk.constants.len(); + let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); + let challenge_mptr = permutation_comm_mptr + 2 * vk.permutation_comms.len(); + let theta_mptr = challenge_mptr + meta.challenge_indices.len(); + + let advice_comm_start = proof_cptr; + let lookup_permuted_comm_start = advice_comm_start + 2 * meta.advice_indices.len(); + let permutation_z_comm_start = lookup_permuted_comm_start + 2 * meta.num_lookup_permuteds; + let lookup_z_comm_start = permutation_z_comm_start + 2 * meta.num_permutation_zs; + let random_comm_start = lookup_z_comm_start + 2 * meta.num_lookup_zs; + let quotient_comm_start = random_comm_start + 2; + + let eval_cptr = quotient_comm_start + 2 * meta.num_quotients; + let advice_eval_cptr = eval_cptr; + let fixed_eval_cptr = advice_eval_cptr + meta.advice_queries.len(); + let random_eval_cptr = fixed_eval_cptr + meta.fixed_queries.len(); + let permutation_eval_cptr = random_eval_cptr + 1; + let permutation_z_eval_cptr = permutation_eval_cptr + meta.num_permutations(); + let lookup_eval_cptr = permutation_z_eval_cptr + 3 * meta.num_permutation_zs - 1; + let w_cptr = lookup_eval_cptr + 5 * meta.num_lookups(); + + let fixed_comms = EcPoint::range(fixed_comm_mptr) + .take(meta.num_fixeds) + .collect(); + let permutation_comms = izip!( + meta.permutation_columns.iter().cloned(), + EcPoint::range(permutation_comm_mptr) + ) + .collect(); + let advice_comms = meta + .advice_indices + .iter() + .map(|idx| advice_comm_start + 2 * idx) + .map_into() + .collect(); + let lookup_permuted_comms = EcPoint::range(lookup_permuted_comm_start) + .take(meta.num_lookup_permuteds) + .tuples() + .collect(); + let permutation_z_comms = EcPoint::range(permutation_z_comm_start) + .take(meta.num_permutation_zs) + .collect(); + let lookup_z_comms = EcPoint::range(lookup_z_comm_start) + .take(meta.num_lookup_zs) + .collect(); + let random_comm = random_comm_start.into(); + let computed_quotient_comm = EcPoint::new( + Ptr::memory("QUOTIENT_X_MPTR"), + Ptr::memory("QUOTIENT_Y_MPTR"), + ); + + let challenges = meta + .challenge_indices + .iter() + .map(|idx| challenge_mptr + *idx) + .map_into() + .collect_vec(); + let instance_eval = Ptr::memory("INSTANCE_EVAL_MPTR").into(); + let advice_evals = izip!( + meta.advice_queries.iter().cloned(), + Word::range(advice_eval_cptr) + ) + .collect(); + let fixed_evals = izip!( + meta.fixed_queries.iter().cloned(), + Word::range(fixed_eval_cptr) + ) + .collect(); + let random_eval = random_eval_cptr.into(); + let permutation_evals = izip!( + meta.permutation_columns.iter().cloned(), + Word::range(permutation_eval_cptr) + ) + .collect(); + let permutation_z_evals = Word::range(permutation_z_eval_cptr) + .take(3 * meta.num_permutation_zs) + .tuples() + .collect_vec(); + let lookup_evals = Word::range(lookup_eval_cptr) + .take(5 * meta.num_lookup_zs) + .tuples() + .collect_vec(); + let computed_quotient_eval = Ptr::memory("QUOTIENT_EVAL_MPTR").into(); + + Self { + challenge_mptr, + theta_mptr, + quotient_comm_cptr: quotient_comm_start, + w_cptr, + + fixed_comms, + permutation_comms, + advice_comms, + lookup_permuted_comms, + permutation_z_comms, + lookup_z_comms, + random_comm, + computed_quotient_comm, + + challenges, + + instance_eval, + advice_evals, + fixed_evals, + permutation_evals, + permutation_z_evals, + lookup_evals, + random_eval, + computed_quotient_eval, + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum Location { Calldata, diff --git a/src/test.rs b/src/test.rs index ee491b8..7ff0462 100644 --- a/src/test.rs +++ b/src/test.rs @@ -343,6 +343,7 @@ mod halo2 { } fn configure(meta: &mut ConstraintSystem) -> Self::Config { + #[cfg(feature = "mv-lookup")] meta.set_minimum_degree(9); let selectors = [(); 10].map(|_| meta.selector()); @@ -351,15 +352,15 @@ mod halo2 { let (advices, challenges) = (0..10) .map(|idx| match idx % 3 { 0 => ( - meta.advice_column_in(FirstPhase), + meta.advice_column_in(FirstPhase, true), meta.challenge_usable_after(FirstPhase), ), 1 => ( - meta.advice_column_in(SecondPhase), + meta.advice_column_in(SecondPhase, true), meta.challenge_usable_after(SecondPhase), ), 2 => ( - meta.advice_column_in(ThirdPhase), + meta.advice_column_in(ThirdPhase, true), meta.challenge_usable_after(ThirdPhase), ), _ => unreachable!(), @@ -407,6 +408,7 @@ mod halo2 { }); } + #[cfg(feature = "mv-lookup")] for _ in 0..10 { meta.lookup_any("", |meta| { let (q1, q2, q3) = complex_selectors.iter().tuple_windows().next().unwrap(); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 81b2db6..7abfb0b 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -13,12 +13,12 @@ contract Halo2Verifier { uint256 internal constant VK_MPTR = {{ vk_mptr }}; uint256 internal constant VK_DIGEST_MPTR = {{ vk_mptr }}; - uint256 internal constant K_MPTR = {{ vk_mptr + 1 }}; - uint256 internal constant N_INV_MPTR = {{ vk_mptr + 2 }}; - uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 3 }}; - uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 4 }}; - uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 5 }}; - uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 6 }}; + uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 1 }}; + uint256 internal constant K_MPTR = {{ vk_mptr + 2 }}; + uint256 internal constant N_INV_MPTR = {{ vk_mptr + 3 }}; + uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 4 }}; + uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 5 }}; + uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 6 }}; uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 7 }}; uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 8 }}; uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 9 }}; @@ -70,10 +70,10 @@ contract Halo2Verifier { uint256 internal constant PAIRING_RHS_Y_MPTR = {{ theta_mptr + 25 }}; function verifyProof( - {%- match vk %} - {%- when Some with (vk) %} + {%- match self.embedded_vk %} {%- when None %} address vk, + {%- else %} {%- endmatch %} bytes calldata proof, uint256[] calldata instances @@ -223,25 +223,15 @@ contract Halo2Verifier { let success := true { - {%- match vk %} - {%- when Some with (vk) %} - // Load vk into memory - {%- for (name, chunk) in vk.constants %} + {%- match self.embedded_vk %} + {%- when Some with (embedded_vk) %} + // Load vk_digest and num_instances of vk into memory + {%- for (name, chunk) in embedded_vk.constants[..2] %} mstore({{ vk_mptr + loop.index0 }}, {{ chunk|hex_padded(64) }}) // {{ name }} {%- endfor %} - {%- for (x, y) in vk.fixed_comms %} - {%- let offset = vk.constants.len() %} - mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x - mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y - {%- endfor %} - {%- for (x, y) in vk.permutation_comms %} - {%- let offset = vk.constants.len() + 2 * vk.fixed_comms.len() %} - mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x - mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y - {%- endfor %} {%- when None %} - // Copy vk into memory - extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + // Copy vk_digest and num_instances of vk into memory + extcodecopy(vk, VK_MPTR, 0x00, 0x40) {%- endmatch %} // Check valid length of proof @@ -272,7 +262,6 @@ contract Halo2Verifier { let proof_cptr := PROOF_CPTR let challenge_mptr := CHALLENGE_MPTR {%- for num_advices in num_advices %} - {%- let num_challenges = num_challenges[loop.index0] %} // Phase {{ loop.index }} for @@ -284,7 +273,7 @@ contract Halo2Verifier { } challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) - {%- for _ in 0..num_challenges - 1 %} + {%- for _ in 0..num_challenges[loop.index0] - 1 %} challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) {%- endfor %} {%- endfor %} @@ -317,6 +306,27 @@ contract Halo2Verifier { // TODO {%- endmatch %} + {%~ match self.embedded_vk %} + {%- when Some with (embedded_vk) %} + // Load full vk into memory + {%- for (name, chunk) in embedded_vk.constants %} + mstore({{ vk_mptr + loop.index0 }}, {{ chunk|hex_padded(64) }}) // {{ name }} + {%- endfor %} + {%- for (x, y) in embedded_vk.fixed_comms %} + {%- let offset = embedded_vk.constants.len() %} + mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x + mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y + {%- endfor %} + {%- for (x, y) in embedded_vk.permutation_comms %} + {%- let offset = embedded_vk.constants.len() + 2 * embedded_vk.fixed_comms.len() %} + mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x + mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y + {%- endfor %} + {%- when None %} + // Copy full vk into memory + extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + {%- endmatch %} + // Read accumulator from instances if mload(HAS_ACCUMULATOR_MPTR) { let num_limbs := mload(NUM_ACC_LIMBS_MPTR) From eb04be1f7d005e5b9dd3ff41efa30aeb5e0c34a3 Mon Sep 17 00:00:00 2001 From: Alexander Camuto Date: Mon, 22 Jan 2024 17:56:34 +0000 Subject: [PATCH 03/65] fix: tests --- src/test.rs | 74 ++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/test.rs b/src/test.rs index 7ff0462..e783fd0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -211,17 +211,18 @@ mod halo2 { fn random_accumulator_limbs( acc_encoding: AccumulatorEncoding, mut rng: impl RngCore, - ) -> Vec + ) -> Vec where M: MultiMillerLoop, + M::G1Affine: CurveAffine, ::Base: PrimeField, - M::Scalar: PrimeField, + M::Fr: PrimeField, { - let s = M::Scalar::random(&mut rng); + let s = M::Fr::random(&mut rng); let g1 = M::G1Affine::generator(); let g2 = M::G2Affine::generator(); let neg_s_g2 = (g2 * -s).to_affine(); - let lhs_scalar = M::Scalar::random(&mut rng); + let lhs_scalar = M::Fr::random(&mut rng); let rhs_scalar = lhs_scalar * s.invert().unwrap(); let [lhs, rhs] = [lhs_scalar, rhs_scalar].map(|scalar| (g1 * scalar).to_affine()); @@ -295,13 +296,15 @@ mod halo2 { use std::{array, fmt::Debug, iter, mem}; #[derive(Clone, Debug, Default)] - pub struct HugeCircuit(Vec); + pub struct HugeCircuit(Vec); - impl TestCircuit for HugeCircuit + impl TestCircuit for HugeCircuit where M: MultiMillerLoop, + M::G1Affine: CurveAffine, + ::ScalarExt: PrimeField, ::Base: PrimeField, - M::Scalar: PrimeField, + M::Fr: PrimeField, { fn min_k() -> u32 { 6 @@ -311,21 +314,21 @@ mod halo2 { let instances = if let Some(acc_encoding) = acc_encoding { random_accumulator_limbs::(acc_encoding, rng) } else { - iter::repeat_with(|| M::Scalar::random(&mut rng)) + iter::repeat_with(|| M::Fr::random(&mut rng)) .take(10) .collect() }; Self(instances) } - fn instances(&self) -> Vec { + fn instances(&self) -> Vec { self.0.clone() } } - impl Circuit for HugeCircuit + impl Circuit for HugeCircuit where - M::Scalar: PrimeField, + M::Fr: PrimeField, { type Config = ( [Selector; 10], @@ -342,7 +345,7 @@ mod halo2 { unimplemented!() } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn configure(meta: &mut ConstraintSystem) -> Self::Config { #[cfg(feature = "mv-lookup")] meta.set_minimum_degree(9); @@ -352,15 +355,15 @@ mod halo2 { let (advices, challenges) = (0..10) .map(|idx| match idx % 3 { 0 => ( - meta.advice_column_in(FirstPhase, true), + meta.advice_column_in(FirstPhase), meta.challenge_usable_after(FirstPhase), ), 1 => ( - meta.advice_column_in(SecondPhase, true), + meta.advice_column_in(SecondPhase), meta.challenge_usable_after(SecondPhase), ), 2 => ( - meta.advice_column_in(ThirdPhase, true), + meta.advice_column_in(ThirdPhase), meta.challenge_usable_after(ThirdPhase), ), _ => unreachable!(), @@ -372,7 +375,7 @@ mod halo2 { meta.create_gate("", |meta| { let selectors = selectors.map(|selector| meta.query_selector(selector)); - let advices: [Expression; 10] = array::from_fn(|idx| { + let advices: [Expression; 10] = array::from_fn(|idx| { let rotation = Rotation((idx as i32 - advices.len() as i32) / 2); meta.query_advice(advices[idx], rotation) }); @@ -435,7 +438,7 @@ mod halo2 { fn synthesize( &self, (selectors, complex_selectors, fixeds, advices, instance): Self::Config, - mut layouter: impl Layouter, + mut layouter: impl Layouter, ) -> Result<(), plonk::Error> { let assigneds = layouter.assign_region( || "", @@ -450,7 +453,7 @@ mod halo2 { q.enable(&mut region, next_offset())?; } for (idx, column) in izip!(1.., fixeds) { - let value = Value::known(M::Scalar::from(idx)); + let value = Value::known(M::Fr::from(idx)); region.assign_fixed(|| "", column, next_offset(), || value)?; } izip!(advices, &self.0) @@ -527,14 +530,16 @@ mod halo2 { #[derive(Clone, Default)] pub struct MainGateWithRange { - instances: Vec, + instances: Vec, } - impl TestCircuit for MainGateWithRange + impl TestCircuit for MainGateWithRange where M: MultiMillerLoop, + M::G1Affine: CurveAffine, + ::ScalarExt: PrimeField, ::Base: PrimeField, - M::Scalar: PrimeField, + M::Fr: PrimeField, { fn min_k() -> u32 { 9 @@ -544,21 +549,21 @@ mod halo2 { let instances = if let Some(acc_encoding) = acc_encoding { random_accumulator_limbs::(acc_encoding, rng) } else { - iter::repeat_with(|| M::Scalar::random(&mut rng)) + iter::repeat_with(|| M::Fr::random(&mut rng)) .take(10) .collect() }; Self { instances } } - fn instances(&self) -> Vec { + fn instances(&self) -> Vec { self.instances.clone() } } - impl Circuit for MainGateWithRange + impl Circuit for MainGateWithRange where - M::Scalar: PrimeField, + M::Fr: PrimeField, { type Config = MainGateWithRangeConfig; type FloorPlanner = SimpleFloorPlanner; @@ -569,14 +574,14 @@ mod halo2 { unimplemented!() } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn configure(meta: &mut ConstraintSystem) -> Self::Config { MainGateWithRangeConfig::configure(meta, vec![8], vec![4, 7]) } fn synthesize( &self, config: Self::Config, - mut layouter: impl Layouter, + mut layouter: impl Layouter, ) -> Result<(), Error> { let main_gate = config.main_gate(); let range_chip = config.range_chip(); @@ -596,25 +601,20 @@ mod halo2 { // Dummy gates to make all fixed column with values range_chip.decompose( &mut ctx, - Value::known(M::Scalar::from(u64::MAX)), + Value::known(M::Fr::from(u64::MAX)), 8, 64, )?; range_chip.decompose( &mut ctx, - Value::known(M::Scalar::from(u32::MAX as u64)), + Value::known(M::Fr::from(u32::MAX as u64)), 8, 39, )?; let a = &advices[0]; - let b = main_gate.sub_sub_with_constant( - &mut ctx, - a, - a, - a, - M::Scalar::from(2), - )?; - let cond = main_gate.assign_bit(&mut ctx, Value::known(M::Scalar::ONE))?; + let b = + main_gate.sub_sub_with_constant(&mut ctx, a, a, a, M::Fr::from(2))?; + let cond = main_gate.assign_bit(&mut ctx, Value::known(M::Fr::ONE))?; main_gate.select(&mut ctx, a, &b, &cond)?; Ok(advices) From fd74f1da2ce51664e2d4349965987ee606551060 Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Thu, 2 May 2024 17:07:57 +0100 Subject: [PATCH 04/65] chore: update readme (#4) --------- Co-authored-by: Ethan --- Cargo.toml | 6 +++--- README.md | 5 +++++ examples/separately.rs | 2 ++ src/codegen.rs | 3 +++ src/codegen/template.rs | 1 + src/test.rs | 1 + templates/Halo2Verifier.sol | 4 ++-- 7 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5aca2e1..d689458 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", branch="main" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", branch = "main" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" @@ -20,7 +20,7 @@ revm = { version = "3.3.0", default-features = false, optional = true } [dev-dependencies] rand = "0.8.5" revm = { version = "3.3.0", default-features = false } -halo2_maingate = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "maingate" } +halo2_maingate = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "maingate" } [features] @@ -30,4 +30,4 @@ mv-lookup = ["halo2_proofs/mv-lookup", "halo2_maingate/mv-lookup"] [[example]] name = "separately" -required-features = ["evm"] \ No newline at end of file +required-features = ["evm"] diff --git a/README.md b/README.md index 7cae107..a53f557 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,8 @@ This repository is a ground-up rebuild, addressing these concerns while maintain ## Acknowledgement The template is heavily inspired by Aztec's [`BaseUltraVerifier.sol`](https://github.com/AztecProtocol/barretenberg/blob/4c456a2b196282160fd69bead6a1cea85289af37/sol/src/ultra/BaseUltraVerifier.sol). + + +## Lookup Modularity + +Note that we have extended the verifier to include the ability to verify mvlookup / logup lookups. This is hidden behind the `mvlookup` feature flag. \ No newline at end of file diff --git a/examples/separately.rs b/examples/separately.rs index b2e58c5..89c3ed8 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -1,6 +1,7 @@ use application::StandardPlonk; use prelude::*; +use halo2_proofs::poly::commitment::Params; use halo2_solidity_verifier::{ compile_solidity, encode_calldata, BatchOpenScheme::Bdfg21, Evm, Keccak256Transcript, SolidityGenerator, @@ -107,6 +108,7 @@ fn create_proof_checked( SingleStrategy::new(params), &[&[instances]], &mut transcript, + params.n(), ) }; assert!(result.is_ok()); diff --git a/src/codegen.rs b/src/codegen.rs index d950c27..0b012ae 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -235,6 +235,8 @@ impl<'a> SolidityGenerator<'a> { fn generate_verifier(&self, separate: bool) -> Halo2Verifier { let proof_cptr = Ptr::calldata(if separate { 0x84 } else { 0x64 }); + let proof_len_cptr = Ptr::calldata(if separate { 0x6014F51964 } else { 0x6014F51944 }); + let vk = self.generate_vk(); let vk_len = vk.len(); let vk_mptr = Ptr::memory(self.estimate_static_working_memory_size(&vk, proof_cptr)); @@ -276,6 +278,7 @@ impl<'a> SolidityGenerator<'a> { num_evals: self.meta.num_evals, num_quotients: self.meta.num_quotients, proof_cptr, + proof_len_cptr, quotient_comm_cptr: data.quotient_comm_cptr, proof_len: self.meta.proof_len(self.scheme), challenge_mptr: data.challenge_mptr, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 55753f9..b1ce38c 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -32,6 +32,7 @@ pub(crate) struct Halo2Verifier { pub(crate) challenge_mptr: Ptr, pub(crate) theta_mptr: Ptr, pub(crate) proof_cptr: Ptr, + pub(crate) proof_len_cptr: Ptr, pub(crate) quotient_comm_cptr: Ptr, pub(crate) num_neg_lagranges: usize, pub(crate) num_advices: Vec, diff --git a/src/test.rs b/src/test.rs index e783fd0..75b631c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -201,6 +201,7 @@ mod halo2 { SingleStrategy::new(¶ms), &[&[&instances]], &mut transcript, + 1 << k, ) }; assert!(result.is_ok()); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 7abfb0b..e94587a 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; contract Halo2Verifier { - uint256 internal constant PROOF_LEN_CPTR = {{ proof_cptr - 1 }}; + uint256 internal constant PROOF_LEN_CPTR = {{ proof_len_cptr }}; uint256 internal constant PROOF_CPTR = {{ proof_cptr }}; uint256 internal constant NUM_INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) }}; uint256 internal constant INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) + 1 }}; @@ -235,7 +235,7 @@ contract Halo2Verifier { {%- endmatch %} // Check valid length of proof - success := and(success, eq({{ proof_len|hex() }}, calldataload(PROOF_LEN_CPTR))) + success := and(success, eq({{ proof_len|hex() }}, calldataload(sub(PROOF_LEN_CPTR, 0x6014F51900)))) // Check valid length of instances let num_instances := mload(NUM_INSTANCES_MPTR) From 270112550a3e92ee8224aff9f60bdd16068c3cfa Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 24 Jun 2024 00:23:27 -0500 Subject: [PATCH 05/65] append lookup consts to VK memory. --- src/codegen.rs | 27 +++++++++++++-- src/codegen/evaluator.rs | 61 ++++++++++++++++++++++++++------- src/codegen/template.rs | 2 ++ templates/Halo2VerifyingKey.sol | 7 ++-- 4 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 0b012ae..2e68b69 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -217,18 +217,39 @@ impl<'a> SolidityGenerator<'a> { ("neg_s_g2_y_2", neg_s_g2[3]), ] }; - let fixed_comms = chain![self.vk.fixed_commitments()] + let fixed_comms: Vec<(U256, U256)> = chain![self.vk.fixed_commitments()] .flat_map(g1_to_u256s) .tuples() .collect(); - let permutation_comms = chain![self.vk.permutation().commitments()] + let permutation_comms: Vec<(U256, U256)> = chain![self.vk.permutation().commitments()] .flat_map(g1_to_u256s) .tuples() .collect(); + + let proof_cptr = Ptr::calldata(if true { 0x84 } else { 0x64 }); + + let dummy_vk = Halo2VerifyingKey { + constants: constants.clone(), + fixed_comms: fixed_comms.clone(), + permutation_comms: permutation_comms.clone(), + const_lookup_input_expressions: vec![], + }; + + let vk_mptr = Ptr::memory(self.estimate_static_working_memory_size(&dummy_vk, proof_cptr)); + let data = Data::new(&self.meta, &dummy_vk, vk_mptr, proof_cptr); + + let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); + + let result: (Vec<(Vec, String)>, Vec) = evaluator.lookup_computations(); + + let const_lookup_input_expressions = + result.1.into_iter().map(fr_to_u256).collect::>(); + Halo2VerifyingKey { constants, fixed_comms, permutation_comms, + const_lookup_input_expressions, } } @@ -246,7 +267,7 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations() + evaluator.lookup_computations().0 ] .enumerate() .map(|(idx, (mut lines, var))| { diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 39428b9..2ae05fc 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -121,8 +121,9 @@ where } #[cfg(feature = "mv-lookup")] - pub fn lookup_computations(&self) -> Vec<(Vec, String)> { + pub fn lookup_computations(&self) -> (Vec<(Vec, String)>, Vec) { let evaluate = |expressions: &Vec<_>| { + println!("expressions: {:?}", expressions); let (lines, inputs) = expressions .iter() .map(|expression| self.evaluate(expression)) @@ -134,26 +135,36 @@ where self.reset(); (lines, inputs) }; + + let evaluate_lookup_consts = |expressions: &Vec<_>, constants: &mut Vec| { + expressions.iter().for_each(|expression| { + self.collect_constants(expression, constants); + }); + }; + + let mut inputs_consts: Vec = Vec::new(); let inputs_tables = self .cs .lookups() .iter() .map(|lookup| { - let inputs = lookup - .input_expressions() - .iter() - .map(evaluate) - .collect_vec(); + let inputs_iter = lookup.input_expressions().iter(); + let inputs = inputs_iter.clone().map(evaluate).collect_vec(); + inputs_iter.for_each(|arg| { + evaluate_lookup_consts(arg, &mut inputs_consts); + }); let table = evaluate(lookup.table_expressions()); (inputs, table) }) .collect_vec(); - izip!(inputs_tables, &self.data.lookup_evals) + let vec = izip!(inputs_tables, &self.data.lookup_evals) .flat_map(|(inputs_tables, evals)| { - let (inputs, (table_lines, tables)) = inputs_tables; + let (inputs, (table_lines, tables)) = inputs_tables.clone(); let num_inputs = inputs.len(); let (table_0, rest_tables) = tables.split_first().unwrap(); let (phi, phi_next, m) = evals; + // print line the input tables + println!("inputs: {:?}", inputs.clone()); [ vec![ format!("let l_0 := mload(L_0_MPTR)"), @@ -238,11 +249,12 @@ where ] }) .zip(iter::repeat("eval".to_string())) - .collect_vec() + .collect_vec(); + (vec, inputs_consts) } #[cfg(not(feature = "mv-lookup"))] - pub fn lookup_computations(&self) -> Vec<(Vec, String)> { + pub fn lookup_computations(&self) -> (Vec<(Vec, String)>, Vec) { let input_tables = self .cs .lookups() @@ -264,7 +276,7 @@ where (input_lines, inputs, table_lines, tables) }) .collect_vec(); - izip!(input_tables, &self.data.lookup_evals) + let vec = izip!(input_tables, &self.data.lookup_evals) .flat_map(|(input_table, evals)| { let (input_lines, inputs, table_lines, tables) = input_table; let (input_0, rest_inputs) = inputs.split_first().unwrap(); @@ -338,7 +350,8 @@ where ] }) .zip(iter::repeat("eval".to_string())) - .collect_vec() + .collect_vec(); + (vec, Vec::new()) } fn eval(&self, column_type: impl Into, column_index: usize, rotation: i32) -> String { @@ -360,6 +373,30 @@ where result } + #[allow(dead_code)] + fn collect_constants(&self, expression: &Expression, constants: &mut Vec) { + match expression { + Expression::Constant(constant) => { + constants.push(*constant); + } + Expression::Negated(inner) => { + self.collect_constants(inner, constants); + } + Expression::Sum(lhs, rhs) => { + self.collect_constants(lhs, constants); + self.collect_constants(rhs, constants); + } + Expression::Product(lhs, rhs) => { + self.collect_constants(lhs, constants); + self.collect_constants(rhs, constants); + } + Expression::Scaled(inner, _scalar) => { + self.collect_constants(inner, constants); + } + _ => {} + } + } + fn evaluate(&self, expression: &Expression) -> (Vec, String) { evaluate( expression, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index b1ce38c..d4886ca 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -12,12 +12,14 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) constants: Vec<(&'static str, U256)>, pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, + pub(crate) const_lookup_input_expressions: Vec, } impl Halo2VerifyingKey { pub(crate) fn len(&self) -> usize { (self.constants.len() * 0x20) + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + + (self.const_lookup_input_expressions.len() * 0x20) } } diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index c9edfce..8b0f856 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -18,8 +18,11 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} - - return(0, {{ (32 * (constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len()))|hex() }}) + {%- for const in const_lookup_input_expressions %} + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_lookup_input_expressions[{{ loop.index0 }}] + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len()))|hex() }}) } } } From 32b59b1938e7b57367ae47584f8b124939ff44f4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 24 Jun 2024 08:06:51 -0500 Subject: [PATCH 06/65] * finish up tests --- Cargo.toml | 1 + src/codegen.rs | 35 +++++++++++++++++--- src/codegen/evaluator.rs | 71 +++++++++++++++++++++++++++++++++++++--- src/codegen/util.rs | 4 ++- 4 files changed, 101 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d689458..2c28973 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ hex = "0.4.3" ruint = "1.8.0" sha3 = "0.10" itertools = "0.11.0" +regex = { version = "1", default_features = false } # Remove when `vk.transcript_repr()` is ready for usage. blake2b_simd = "1" diff --git a/src/codegen.rs b/src/codegen.rs index 2e68b69..1cd389a 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -14,7 +14,10 @@ use halo2_proofs::{ }; use itertools::{chain, Itertools}; use ruint::aliases::U256; -use std::fmt::{self, Debug}; +use std::{ + collections::HashMap, + fmt::{self, Debug}, +}; mod evaluator; mod pcs; @@ -240,7 +243,8 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - let result: (Vec<(Vec, String)>, Vec) = evaluator.lookup_computations(); + let result: (Vec<(Vec, String)>, Vec) = + evaluator.lookup_computations(None); let const_lookup_input_expressions = result.1.into_iter().map(fr_to_u256).collect::>(); @@ -260,14 +264,37 @@ impl<'a> SolidityGenerator<'a> { let vk = self.generate_vk(); let vk_len = vk.len(); - let vk_mptr = Ptr::memory(self.estimate_static_working_memory_size(&vk, proof_cptr)); + let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); + let vk_mptr = Ptr::memory(vk_m); + let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); + // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. + if separate { + // create hashmap of vk.const_lookup_input_expressions values to its vk memory location. + let offset = vk_len + vk_m - (vk.const_lookup_input_expressions.len() * 0x20); + print!("offset: {:?}\n", offset); + print!("vk_mptr: {:?}\n", vk_m); + print!( + "vk.const_lookup_input_expressions: {:?}\n", + vk.const_lookup_input_expressions + ); + println!("vk_len {:?}", vk_len); + // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. + vk.const_lookup_input_expressions + .iter() + .enumerate() + .for_each(|(idx, _)| { + let mptr = offset + (0x20 * idx); + let mptr = Ptr::memory(mptr); + vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); + }); + } let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations().0 + evaluator.lookup_computations(Some(vk_lookup_const_table)).0 ] .enumerate() .map(|(idx, (mut lines, var))| { diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 2ae05fc..60c12f8 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -9,6 +9,7 @@ use halo2_proofs::{ }, }; use itertools::{chain, izip, Itertools}; +use regex::Regex; use ruint::aliases::U256; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; @@ -121,9 +122,12 @@ where } #[cfg(feature = "mv-lookup")] - pub fn lookup_computations(&self) -> (Vec<(Vec, String)>, Vec) { + pub fn lookup_computations( + &self, + vk_lookup_const_table: Option, super::util::Ptr>>, + ) -> (Vec<(Vec, String)>, Vec) { let evaluate = |expressions: &Vec<_>| { - println!("expressions: {:?}", expressions); + // println!("expressions: {:?}", expressions); let (lines, inputs) = expressions .iter() .map(|expression| self.evaluate(expression)) @@ -157,6 +161,24 @@ where (inputs, table) }) .collect_vec(); + // Remove duplicates while preserving order + let mut unique_inputs_consts = Vec::new(); + for const_value in inputs_consts.clone() { + if !unique_inputs_consts.contains(&const_value) { + unique_inputs_consts.push(const_value); + } + } + let lookup_const_table = if let Some(vk_lookup_const_table) = vk_lookup_const_table { + // map all the keys to u256_string + let vk_lookup_const_table: HashMap = vk_lookup_const_table + .iter() + .map(|(key, value)| (u256_string(*key), *value)) + .collect(); + Some(vk_lookup_const_table) + } else { + None + }; + let vec = izip!(inputs_tables, &self.data.lookup_evals) .flat_map(|(inputs_tables, evals)| { let (inputs, (table_lines, tables)) = inputs_tables.clone(); @@ -164,7 +186,6 @@ where let (table_0, rest_tables) = tables.split_first().unwrap(); let (phi, phi_next, m) = evals; // print line the input tables - println!("inputs: {:?}", inputs.clone()); [ vec![ format!("let l_0 := mload(L_0_MPTR)"), @@ -192,6 +213,37 @@ where izip!(0.., inputs.into_iter()).flat_map(|(idx, (input_lines, inputs))| { let (input_0, rest_inputs) = inputs.split_first().unwrap(); let ident = format!("input_{idx}"); + let hex_regex = Regex::new(r":= (0x[0-9a-fA-F]+)").unwrap(); + // use regex to replace hex constants with mload format + let input_lines = + if let Some(lookup_const_table) = lookup_const_table.clone() { + // println!("lookup_const_table: {:?}", lookup_const_table); + let modified_input_lines: Vec = input_lines + .into_iter() + .map(|line| { + hex_regex + .replace_all(&line, |caps: ®ex::Captures| { + if let Some(hex_str) = caps.get(1) { + println!("hex_str: {:?}", hex_str); + if let Some(ptr) = + lookup_const_table.get(hex_str.as_str()) + { + format!(":= mload({ptr})") + } else { + hex_str.as_str().to_string() + } + } else { + line.to_string() + } + }) + .to_string() + }) + .collect(); + modified_input_lines + } else { + input_lines + }; + // use regex to chain![ [format!("let {ident}")], code_block::<1, false>(chain![ @@ -250,11 +302,19 @@ where }) .zip(iter::repeat("eval".to_string())) .collect_vec(); - (vec, inputs_consts) + (vec, unique_inputs_consts) } #[cfg(not(feature = "mv-lookup"))] - pub fn lookup_computations(&self) -> (Vec<(Vec, String)>, Vec) { + pub fn lookup_computations( + &self, + _vk_lookup_const_table: Option, super::util::Ptr>>, + ) -> (Vec<(Vec, String)>, Vec) { + let _ = |expressions: &Vec<_>, constants: &mut Vec| { + expressions.iter().for_each(|expression| { + self.collect_constants(expression, constants); + }); + }; let input_tables = self .cs .lookups() @@ -373,6 +433,7 @@ where result } + #[allow(clippy::only_used_in_recursion)] #[allow(dead_code)] fn collect_constants(&self, expression: &Expression, constants: &mut Vec) { match expression { diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 1f31ea7..7c3d5cd 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -397,7 +397,9 @@ impl Data { ) -> Self { let fixed_comm_mptr = vk_mptr + vk.constants.len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); - let challenge_mptr = permutation_comm_mptr + 2 * vk.permutation_comms.len(); + let challenge_mptr = permutation_comm_mptr + + (2 * vk.permutation_comms.len()) + + vk.const_lookup_input_expressions.len(); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; From 0aa84ea8306cb353e139103da5ad0bbb0aa9b553 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 25 Jun 2024 11:58:17 -0500 Subject: [PATCH 07/65] *pass none to `evaluator.lookup_computations` when building conjoined verifier. --- src/codegen.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 1cd389a..0e10c65 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -266,18 +266,11 @@ impl<'a> SolidityGenerator<'a> { let vk_len = vk.len(); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); let vk_mptr = Ptr::memory(vk_m); - let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. - if separate { + let vk_lookup_const_table: Option, Ptr>> = if separate { + let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); // create hashmap of vk.const_lookup_input_expressions values to its vk memory location. let offset = vk_len + vk_m - (vk.const_lookup_input_expressions.len() * 0x20); - print!("offset: {:?}\n", offset); - print!("vk_mptr: {:?}\n", vk_m); - print!( - "vk.const_lookup_input_expressions: {:?}\n", - vk.const_lookup_input_expressions - ); - println!("vk_len {:?}", vk_len); // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. vk.const_lookup_input_expressions .iter() @@ -287,14 +280,17 @@ impl<'a> SolidityGenerator<'a> { let mptr = Ptr::memory(mptr); vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); }); - } + Some(vk_lookup_const_table) + } else { + None + }; let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations(Some(vk_lookup_const_table)).0 + evaluator.lookup_computations(vk_lookup_const_table).0 ] .enumerate() .map(|(idx, (mut lines, var))| { From e649d022bcd683c581ff99052935cca686d6a81e Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 25 Jun 2024 12:53:03 -0500 Subject: [PATCH 08/65] check rust version of CI. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5146421..4fec8ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,9 @@ jobs: with: toolchain: stable profile: minimal + + - name: Check Rust version + run: rustc --version && cargo --version - uses: Swatinem/rust-cache@v1 with: From 8e8e5d3a42909ef6475ba4d370a70ec53c6b4ff1 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 25 Jun 2024 13:09:37 -0500 Subject: [PATCH 09/65] *setup toolchain for rust v 1.77.2 for revm compatibility. --- .github/workflows/ci.yml | 5 +---- rust-toolchain | 2 ++ src/codegen/evaluator.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 rust-toolchain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fec8ff..8f96586 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,8 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: 1.77.2 profile: minimal - - - name: Check Rust version - run: rustc --version && cargo --version - uses: Swatinem/rust-cache@v1 with: diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..6f14058 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.77.2" diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 60c12f8..d34b20d 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -224,7 +224,6 @@ where hex_regex .replace_all(&line, |caps: ®ex::Captures| { if let Some(hex_str) = caps.get(1) { - println!("hex_str: {:?}", hex_str); if let Some(ptr) = lookup_const_table.get(hex_str.as_str()) { From a35d85788aaa7806a9475e21e71ae55bd4a514d8 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 1 Jul 2024 17:07:35 -0500 Subject: [PATCH 10/65] *new template for reusable verifier. *cache num_advices and use challenges in vk. --- src/codegen.rs | 164 +++++++-- src/codegen/template.rs | 33 +- src/codegen/util.rs | 3 +- src/test.rs | 4 +- templates/Halo2Verifier.sol | 18 +- templates/Halo2VerifierReusable.sol | 534 ++++++++++++++++++++++++++++ templates/Halo2VerifyingKey.sol | 9 +- 7 files changed, 707 insertions(+), 58 deletions(-) create mode 100644 templates/Halo2VerifierReusable.sol diff --git a/src/codegen.rs b/src/codegen.rs index 0e10c65..36d85e7 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -18,6 +18,7 @@ use std::{ collections::HashMap, fmt::{self, Debug}, }; +use template::Halo2VerifierReusable; mod evaluator; mod pcs; @@ -137,7 +138,7 @@ impl<'a> SolidityGenerator<'a> { impl<'a> SolidityGenerator<'a> { /// Render `Halo2Verifier.sol` with verifying key embedded into writer. pub fn render_into(&self, verifier_writer: &mut impl fmt::Write) -> Result<(), fmt::Error> { - self.generate_verifier(false).render(verifier_writer) + self.generate_verifier().render(verifier_writer) } /// Render `Halo2Verifier.sol` with verifying key embedded and return it as `String`. @@ -153,8 +154,8 @@ impl<'a> SolidityGenerator<'a> { verifier_writer: &mut impl fmt::Write, vk_writer: &mut impl fmt::Write, ) -> Result<(), fmt::Error> { - self.generate_verifier(true).render(verifier_writer)?; - self.generate_vk().render(vk_writer)?; + self.generate_separate_verifier().render(verifier_writer)?; + self.generate_vk(true).render(vk_writer)?; Ok(()) } @@ -166,8 +167,8 @@ impl<'a> SolidityGenerator<'a> { Ok((verifier_output, vk_output)) } - fn generate_vk(&self) -> Halo2VerifyingKey { - let constants = { + fn generate_vk(&self, separate: bool) -> Halo2VerifyingKey { + let mut constants = { let domain = self.vk.get_domain(); let vk_digest = fr_to_u256(vk_transcript_repr(self.vk)); let num_instances = U256::from(self.num_instances); @@ -196,6 +197,7 @@ impl<'a> SolidityGenerator<'a> { let g1 = g1_to_u256s(g1); let g2 = g2_to_u256s(self.params.g2()); let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); + vec![ ("vk_digest", vk_digest), ("num_instances", num_instances), @@ -229,17 +231,23 @@ impl<'a> SolidityGenerator<'a> { .tuples() .collect(); - let proof_cptr = Ptr::calldata(if true { 0x84 } else { 0x64 }); + let proof_cptr = Ptr::calldata(0x64); - let dummy_vk = Halo2VerifyingKey { + let attached_vk = Halo2VerifyingKey { constants: constants.clone(), fixed_comms: fixed_comms.clone(), permutation_comms: permutation_comms.clone(), const_lookup_input_expressions: vec![], + num_advices_user_challenges: vec![], }; - let vk_mptr = Ptr::memory(self.estimate_static_working_memory_size(&dummy_vk, proof_cptr)); - let data = Data::new(&self.meta, &dummy_vk, vk_mptr, proof_cptr); + if !separate { + return attached_vk; + } + + let vk_mptr = + Ptr::memory(self.estimate_static_working_memory_size(&attached_vk, proof_cptr)); + let data = Data::new(&self.meta, &attached_vk, vk_mptr, proof_cptr); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); @@ -249,48 +257,61 @@ impl<'a> SolidityGenerator<'a> { let const_lookup_input_expressions = result.1.into_iter().map(fr_to_u256).collect::>(); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + + (fixed_comms.len() + permutation_comms.len()) * 0x40 + + (const_lookup_input_expressions.len() * 0x20) + + 0x20; + + // insert it at position 3 of the constants. + constants.insert( + 2, + ( + "num_advices_user_challenges_offset", + U256::from(num_advices_user_challenges_offset), + ), + ); + + let num_advices = self.meta.num_advices(); + let num_user_challenges = self.meta.num_challenges(); + // truncate the last elements of num_user_challenges to match the length of num_advices. + let num_user_challenges = num_user_challenges + .iter() + .take(num_advices.len()) + .copied() + .collect::>(); + // Create a new vec of type of Vec with the values of num_advices and num_user_challenges. + let num_advices_user_challenges: Vec<(U256, U256)> = num_advices + .iter() + .zip(num_user_challenges.iter()) + .map(|(num_advices, num_user_challenges)| { + (U256::from(*num_advices), U256::from(*num_user_challenges)) + }) + .collect_vec(); + Halo2VerifyingKey { constants, fixed_comms, permutation_comms, const_lookup_input_expressions, + num_advices_user_challenges, } } - fn generate_verifier(&self, separate: bool) -> Halo2Verifier { - let proof_cptr = Ptr::calldata(if separate { 0x84 } else { 0x64 }); + fn generate_verifier(&self) -> Halo2Verifier { + let proof_cptr = Ptr::calldata(0x64); - let proof_len_cptr = Ptr::calldata(if separate { 0x6014F51964 } else { 0x6014F51944 }); + let proof_len_cptr = Ptr::calldata(0x6014F51944); - let vk = self.generate_vk(); - let vk_len = vk.len(); + let vk = self.generate_vk(false); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); let vk_mptr = Ptr::memory(vk_m); - // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. - let vk_lookup_const_table: Option, Ptr>> = if separate { - let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); - // create hashmap of vk.const_lookup_input_expressions values to its vk memory location. - let offset = vk_len + vk_m - (vk.const_lookup_input_expressions.len() * 0x20); - // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. - vk.const_lookup_input_expressions - .iter() - .enumerate() - .for_each(|(idx, _)| { - let mptr = offset + (0x20 * idx); - let mptr = Ptr::memory(mptr); - vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); - }); - Some(vk_lookup_const_table) - } else { - None - }; let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations(vk_lookup_const_table).0 + evaluator.lookup_computations(None).0 ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -313,8 +334,7 @@ impl<'a> SolidityGenerator<'a> { Halo2Verifier { scheme: self.scheme, - embedded_vk: (!separate).then_some(vk), - vk_len, + embedded_vk: vk, vk_mptr, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, num_advices: self.meta.num_advices(), @@ -332,6 +352,80 @@ impl<'a> SolidityGenerator<'a> { } } + fn generate_separate_verifier(&self) -> Halo2VerifierReusable { + let proof_cptr = Ptr::calldata(0x84); + + let proof_len_cptr = Ptr::calldata(0x6014F51964); + + let vk = self.generate_vk(true); + let vk_len = vk.len(); + let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); + let vk_mptr = Ptr::memory(vk_m); + // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. + let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); + // create hashmap of vk.const_lookup_input_expressions values to its vk memory location. + let offset = vk_m + + (vk.constants.len() * 0x20) + + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; + let arr_mptr = vk_m + + (vk.constants.len() * 0x20) + + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40 + + (vk.const_lookup_input_expressions.len() * 0x20); + // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. + vk.const_lookup_input_expressions + .iter() + .enumerate() + .for_each(|(idx, _)| { + let mptr = offset + (0x20 * idx); + let mptr = Ptr::memory(mptr); + vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); + }); + + let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); + + let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); + let quotient_eval_numer_computations = chain![ + evaluator.gate_computations(), + evaluator.permutation_computations(), + evaluator.lookup_computations(Some(vk_lookup_const_table)).0 + ] + .enumerate() + .map(|(idx, (mut lines, var))| { + let line = if idx == 0 { + format!("quotient_eval_numer := {var}") + } else { + format!( + "quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, r), {var}, r)" + ) + }; + lines.push(line); + lines + }) + .collect(); + + let pcs_computations = match self.scheme { + Bdfg21 => bdfg21_computations(&self.meta, &data), + Gwc19 => unimplemented!(), + }; + + Halo2VerifierReusable { + scheme: self.scheme, + vk_len, + vk_mptr, + num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, + num_evals: self.meta.num_evals, + num_quotients: self.meta.num_quotients, + proof_cptr, + proof_len_cptr, + quotient_comm_cptr: data.quotient_comm_cptr, + proof_len: self.meta.proof_len(self.scheme), + challenge_mptr: data.challenge_mptr, + theta_mptr: data.theta_mptr, + quotient_eval_numer_computations, + pcs_computations, + } + } + fn estimate_static_working_memory_size( &self, vk: &Halo2VerifyingKey, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index d4886ca..1d8b027 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -10,6 +10,7 @@ use std::fmt; #[template(path = "Halo2VerifyingKey.sol")] pub(crate) struct Halo2VerifyingKey { pub(crate) constants: Vec<(&'static str, U256)>, + pub(crate) num_advices_user_challenges: Vec<(U256, U256)>, pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_lookup_input_expressions: Vec, @@ -20,6 +21,7 @@ impl Halo2VerifyingKey { (self.constants.len() * 0x20) + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + (self.const_lookup_input_expressions.len() * 0x20) + + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) } } @@ -27,8 +29,7 @@ impl Halo2VerifyingKey { #[template(path = "Halo2Verifier.sol")] pub(crate) struct Halo2Verifier { pub(crate) scheme: BatchOpenScheme, - pub(crate) embedded_vk: Option, - pub(crate) vk_len: usize, + pub(crate) embedded_vk: Halo2VerifyingKey, pub(crate) proof_len: usize, pub(crate) vk_mptr: Ptr, pub(crate) challenge_mptr: Ptr, @@ -45,6 +46,25 @@ pub(crate) struct Halo2Verifier { pub(crate) pcs_computations: Vec>, } +#[derive(Template)] +#[template(path = "Halo2VerifierReusable.sol")] +pub(crate) struct Halo2VerifierReusable { + pub(crate) scheme: BatchOpenScheme, + pub(crate) vk_len: usize, + pub(crate) proof_len: usize, + pub(crate) vk_mptr: Ptr, + pub(crate) challenge_mptr: Ptr, + pub(crate) theta_mptr: Ptr, + pub(crate) proof_cptr: Ptr, + pub(crate) proof_len_cptr: Ptr, + pub(crate) quotient_comm_cptr: Ptr, + pub(crate) num_neg_lagranges: usize, + pub(crate) num_evals: usize, + pub(crate) num_quotients: usize, + pub(crate) quotient_eval_numer_computations: Vec>, + pub(crate) pcs_computations: Vec>, +} + impl Halo2VerifyingKey { pub(crate) fn render(&self, writer: &mut impl fmt::Write) -> Result<(), fmt::Error> { self.render_into(writer).map_err(|err| match err { @@ -63,6 +83,15 @@ impl Halo2Verifier { } } +impl Halo2VerifierReusable { + pub(crate) fn render(&self, writer: &mut impl fmt::Write) -> Result<(), fmt::Error> { + self.render_into(writer).map_err(|err| match err { + Error::Fmt(err) => err, + _ => unreachable!(), + }) + } +} + mod filters { use std::fmt::LowerHex; diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 7c3d5cd..f6cd46c 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -399,7 +399,8 @@ impl Data { let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); let challenge_mptr = permutation_comm_mptr + (2 * vk.permutation_comms.len()) - + vk.const_lookup_input_expressions.len(); + + vk.const_lookup_input_expressions.len() + + (2 * vk.num_advices_user_challenges.len() + 1); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; diff --git a/src/test.rs b/src/test.rs index 75b631c..33e5d50 100644 --- a/src/test.rs +++ b/src/test.rs @@ -65,7 +65,7 @@ fn run_render>() { let (gas_cost, output) = evm.call(verifier_address, encode_calldata(None, &proof, &instances)); assert_eq!(output, [vec![0; 31], vec![1]].concat()); - println!("Gas cost: {gas_cost}"); + println!("Gas cost conjoined: {gas_cost}"); } fn run_render_separately>() { @@ -105,7 +105,7 @@ fn run_render_separately>() { encode_calldata(Some(vk_address.into()), &proof, &instances), ); assert_eq!(output, [vec![0; 31], vec![1]].concat()); - println!("Gas cost: {gas_cost}"); + println!("Gas cost separate: {gas_cost}"); } } diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index e94587a..22136cb 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -70,11 +70,6 @@ contract Halo2Verifier { uint256 internal constant PAIRING_RHS_Y_MPTR = {{ theta_mptr + 25 }}; function verifyProof( - {%- match self.embedded_vk %} - {%- when None %} - address vk, - {%- else %} - {%- endmatch %} bytes calldata proof, uint256[] calldata instances ) public returns (bool) { @@ -223,16 +218,10 @@ contract Halo2Verifier { let success := true { - {%- match self.embedded_vk %} - {%- when Some with (embedded_vk) %} // Load vk_digest and num_instances of vk into memory {%- for (name, chunk) in embedded_vk.constants[..2] %} mstore({{ vk_mptr + loop.index0 }}, {{ chunk|hex_padded(64) }}) // {{ name }} {%- endfor %} - {%- when None %} - // Copy vk_digest and num_instances of vk into memory - extcodecopy(vk, VK_MPTR, 0x00, 0x40) - {%- endmatch %} // Check valid length of proof success := and(success, eq({{ proof_len|hex() }}, calldataload(sub(PROOF_LEN_CPTR, 0x6014F51900)))) @@ -261,6 +250,7 @@ contract Halo2Verifier { let proof_cptr := PROOF_CPTR let challenge_mptr := CHALLENGE_MPTR + {%- for num_advices in num_advices %} // Phase {{ loop.index }} @@ -306,8 +296,6 @@ contract Halo2Verifier { // TODO {%- endmatch %} - {%~ match self.embedded_vk %} - {%- when Some with (embedded_vk) %} // Load full vk into memory {%- for (name, chunk) in embedded_vk.constants %} mstore({{ vk_mptr + loop.index0 }}, {{ chunk|hex_padded(64) }}) // {{ name }} @@ -322,10 +310,6 @@ contract Halo2Verifier { mstore({{ vk_mptr + offset + 2 * loop.index0 }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x mstore({{ vk_mptr + offset + 2 * loop.index0 + 1 }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} - {%- when None %} - // Copy full vk into memory - extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) - {%- endmatch %} // Read accumulator from instances if mload(HAS_ACCUMULATOR_MPTR) { diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol new file mode 100644 index 0000000..2bc10d6 --- /dev/null +++ b/templates/Halo2VerifierReusable.sol @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract Halo2Verifier { + uint256 internal constant PROOF_LEN_CPTR = {{ proof_len_cptr }}; + uint256 internal constant PROOF_CPTR = {{ proof_cptr }}; + uint256 internal constant NUM_INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) }}; + uint256 internal constant INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) + 1 }}; + + uint256 internal constant FIRST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr }}; + uint256 internal constant LAST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr + 2 * (num_quotients - 1) }}; + + uint256 internal constant VK_MPTR = {{ vk_mptr }}; + uint256 internal constant VK_DIGEST_MPTR = {{ vk_mptr }}; + uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 1 }}; + uint256 internal constant NUM_ADVICES_USER_CHALLENGES_OFFSET = {{ vk_mptr + 2 }}; + uint256 internal constant K_MPTR = {{ vk_mptr + 3 }}; + uint256 internal constant N_INV_MPTR = {{ vk_mptr + 4 }}; + uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 5 }}; + uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 6 }}; + uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 7 }}; + uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 8 }}; + uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 9 }}; + uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 10 }}; + uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 11 }}; + uint256 internal constant G1_X_MPTR = {{ vk_mptr + 12 }}; + uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 13 }}; + uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 14 }}; + uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 15 }}; + uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 16 }}; + uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 17 }}; + uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 18 }}; + uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 19 }}; + uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 20 }}; + uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 21 }}; + + uint256 internal constant CHALLENGE_MPTR = {{ challenge_mptr }}; + + uint256 internal constant THETA_MPTR = {{ theta_mptr }}; + uint256 internal constant BETA_MPTR = {{ theta_mptr + 1 }}; + uint256 internal constant GAMMA_MPTR = {{ theta_mptr + 2 }}; + uint256 internal constant Y_MPTR = {{ theta_mptr + 3 }}; + uint256 internal constant X_MPTR = {{ theta_mptr + 4 }}; + {%- match scheme %} + {%- when Bdfg21 %} + uint256 internal constant ZETA_MPTR = {{ theta_mptr + 5 }}; + uint256 internal constant NU_MPTR = {{ theta_mptr + 6 }}; + uint256 internal constant MU_MPTR = {{ theta_mptr + 7 }}; + {%- when Gwc19 %} + // TODO + {%- endmatch %} + + uint256 internal constant ACC_LHS_X_MPTR = {{ theta_mptr + 8 }}; + uint256 internal constant ACC_LHS_Y_MPTR = {{ theta_mptr + 9 }}; + uint256 internal constant ACC_RHS_X_MPTR = {{ theta_mptr + 10 }}; + uint256 internal constant ACC_RHS_Y_MPTR = {{ theta_mptr + 11 }}; + uint256 internal constant X_N_MPTR = {{ theta_mptr + 12 }}; + uint256 internal constant X_N_MINUS_1_INV_MPTR = {{ theta_mptr + 13 }}; + uint256 internal constant L_LAST_MPTR = {{ theta_mptr + 14 }}; + uint256 internal constant L_BLIND_MPTR = {{ theta_mptr + 15 }}; + uint256 internal constant L_0_MPTR = {{ theta_mptr + 16 }}; + uint256 internal constant INSTANCE_EVAL_MPTR = {{ theta_mptr + 17 }}; + uint256 internal constant QUOTIENT_EVAL_MPTR = {{ theta_mptr + 18 }}; + uint256 internal constant QUOTIENT_X_MPTR = {{ theta_mptr + 19 }}; + uint256 internal constant QUOTIENT_Y_MPTR = {{ theta_mptr + 20 }}; + uint256 internal constant R_EVAL_MPTR = {{ theta_mptr + 21 }}; + uint256 internal constant PAIRING_LHS_X_MPTR = {{ theta_mptr + 22 }}; + uint256 internal constant PAIRING_LHS_Y_MPTR = {{ theta_mptr + 23 }}; + uint256 internal constant PAIRING_RHS_X_MPTR = {{ theta_mptr + 24 }}; + uint256 internal constant PAIRING_RHS_Y_MPTR = {{ theta_mptr + 25 }}; + + function verifyProof( + address vk, + bytes calldata proof, + uint256[] calldata instances + ) public returns (bool) { + assembly { + // Read EC point (x, y) at (proof_cptr, proof_cptr + 0x20), + // and check if the point is on affine plane, + // and store them in (hash_mptr, hash_mptr + 0x20). + // Return updated (success, proof_cptr, hash_mptr). + function read_ec_point(success, proof_cptr, hash_mptr, q) -> ret0, ret1, ret2 { + let x := calldataload(proof_cptr) + let y := calldataload(add(proof_cptr, 0x20)) + ret0 := and(success, lt(x, q)) + ret0 := and(ret0, lt(y, q)) + ret0 := and(ret0, eq(mulmod(y, y, q), addmod(mulmod(x, mulmod(x, x, q), q), 3, q))) + mstore(hash_mptr, x) + mstore(add(hash_mptr, 0x20), y) + ret1 := add(proof_cptr, 0x40) + ret2 := add(hash_mptr, 0x40) + } + + // Squeeze challenge by keccak256(memory[0..hash_mptr]), + // and store hash mod r as challenge in challenge_mptr, + // and push back hash in 0x00 as the first input for next squeeze. + // Return updated (challenge_mptr, hash_mptr). + function squeeze_challenge(challenge_mptr, hash_mptr, r) -> ret0, ret1 { + let hash := keccak256(0x00, hash_mptr) + mstore(challenge_mptr, mod(hash, r)) + mstore(0x00, hash) + ret0 := add(challenge_mptr, 0x20) + ret1 := 0x20 + } + + // Squeeze challenge without absorbing new input from calldata, + // by putting an extra 0x01 in memory[0x20] and squeeze by keccak256(memory[0..21]), + // and store hash mod r as challenge in challenge_mptr, + // and push back hash in 0x00 as the first input for next squeeze. + // Return updated (challenge_mptr). + function squeeze_challenge_cont(challenge_mptr, r) -> ret { + mstore8(0x20, 0x01) + let hash := keccak256(0x00, 0x21) + mstore(challenge_mptr, mod(hash, r)) + mstore(0x00, hash) + ret := add(challenge_mptr, 0x20) + } + + // Batch invert values in memory[mptr_start..mptr_end] in place. + // Return updated (success). + function batch_invert(success, mptr_start, mptr_end, r) -> ret { + let gp_mptr := mptr_end + let gp := mload(mptr_start) + let mptr := add(mptr_start, 0x20) + for + {} + lt(mptr, sub(mptr_end, 0x20)) + {} + { + gp := mulmod(gp, mload(mptr), r) + mstore(gp_mptr, gp) + mptr := add(mptr, 0x20) + gp_mptr := add(gp_mptr, 0x20) + } + gp := mulmod(gp, mload(mptr), r) + + mstore(gp_mptr, 0x20) + mstore(add(gp_mptr, 0x20), 0x20) + mstore(add(gp_mptr, 0x40), 0x20) + mstore(add(gp_mptr, 0x60), gp) + mstore(add(gp_mptr, 0x80), sub(r, 2)) + mstore(add(gp_mptr, 0xa0), r) + ret := and(success, staticcall(gas(), 0x05, gp_mptr, 0xc0, gp_mptr, 0x20)) + let all_inv := mload(gp_mptr) + + let first_mptr := mptr_start + let second_mptr := add(first_mptr, 0x20) + gp_mptr := sub(gp_mptr, 0x20) + for + {} + lt(second_mptr, mptr) + {} + { + let inv := mulmod(all_inv, mload(gp_mptr), r) + all_inv := mulmod(all_inv, mload(mptr), r) + mstore(mptr, inv) + mptr := sub(mptr, 0x20) + gp_mptr := sub(gp_mptr, 0x20) + } + let inv_first := mulmod(all_inv, mload(second_mptr), r) + let inv_second := mulmod(all_inv, mload(first_mptr), r) + mstore(first_mptr, inv_first) + mstore(second_mptr, inv_second) + } + + // Add (x, y) into point at (0x00, 0x20). + // Return updated (success). + function ec_add_acc(success, x, y) -> ret { + mstore(0x40, x) + mstore(0x60, y) + ret := and(success, staticcall(gas(), 0x06, 0x00, 0x80, 0x00, 0x40)) + } + + // Scale point at (0x00, 0x20) by scalar. + function ec_mul_acc(success, scalar) -> ret { + mstore(0x40, scalar) + ret := and(success, staticcall(gas(), 0x07, 0x00, 0x60, 0x00, 0x40)) + } + + // Add (x, y) into point at (0x80, 0xa0). + // Return updated (success). + function ec_add_tmp(success, x, y) -> ret { + mstore(0xc0, x) + mstore(0xe0, y) + ret := and(success, staticcall(gas(), 0x06, 0x80, 0x80, 0x80, 0x40)) + } + + // Scale point at (0x80, 0xa0) by scalar. + // Return updated (success). + function ec_mul_tmp(success, scalar) -> ret { + mstore(0xc0, scalar) + ret := and(success, staticcall(gas(), 0x07, 0x80, 0x60, 0x80, 0x40)) + } + + // Perform pairing check. + // Return updated (success). + function ec_pairing(success, lhs_x, lhs_y, rhs_x, rhs_y) -> ret { + mstore(0x00, lhs_x) + mstore(0x20, lhs_y) + mstore(0x40, mload(G2_X_1_MPTR)) + mstore(0x60, mload(G2_X_2_MPTR)) + mstore(0x80, mload(G2_Y_1_MPTR)) + mstore(0xa0, mload(G2_Y_2_MPTR)) + mstore(0xc0, rhs_x) + mstore(0xe0, rhs_y) + mstore(0x100, mload(NEG_S_G2_X_1_MPTR)) + mstore(0x120, mload(NEG_S_G2_X_2_MPTR)) + mstore(0x140, mload(NEG_S_G2_Y_1_MPTR)) + mstore(0x160, mload(NEG_S_G2_Y_2_MPTR)) + ret := and(success, staticcall(gas(), 0x08, 0x00, 0x180, 0x00, 0x20)) + ret := and(ret, mload(0x00)) + } + + // Modulus + let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // BN254 base field + let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field + + // Initialize success as true + let success := true + + { + // Load vk_digest, num_instances and NUM_ADVICES_USER_CHALLENGES_OFFSET of vk into memory + extcodecopy(vk, VK_MPTR, 0x00, 0x60) + // Check valid length of proof + success := and(success, eq({{ proof_len|hex() }}, calldataload(sub(PROOF_LEN_CPTR, 0x6014F51900)))) + + // Check valid length of instances + let num_instances := mload(NUM_INSTANCES_MPTR) + success := and(success, eq(num_instances, calldataload(NUM_INSTANCE_CPTR))) + + // Absorb vk diegst + mstore(0x00, mload(VK_DIGEST_MPTR)) + + // Read instances and witness commitments and generate challenges + let hash_mptr := 0x20 + let instance_cptr := INSTANCE_CPTR + for + { let instance_cptr_end := add(instance_cptr, mul(0x20, num_instances)) } + lt(instance_cptr, instance_cptr_end) + {} + { + let instance := calldataload(instance_cptr) + success := and(success, lt(instance, r)) + mstore(hash_mptr, instance) + instance_cptr := add(instance_cptr, 0x20) + hash_mptr := add(hash_mptr, 0x20) + } + + let proof_cptr := PROOF_CPTR + let challenge_mptr := CHALLENGE_MPTR + let num_advices_ptr := add(VK_MPTR, mload(NUM_ADVICES_USER_CHALLENGES_OFFSET)) + let num_advices_len := mload(num_advices_ptr) + let advices_ptr := add(num_advices_ptr, 0x20) // start of advices + let challenges_ptr := add(advices_ptr, 0x20) // start of challenges + + // Iterate over phases using the loaded num_advices and num_challenges + for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 1) } { + // Calculate proof_cptr_end based on num_advices + let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, mul(phase, 0x40))))) // We use 0x40 because each advice is followed by the corresponding challenge + + // Phase loop + for { } lt(proof_cptr, proof_cptr_end) { } { + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) + } + + // Generate challenges + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) + + // Continue squeezing challenges based on num_challenges + for { let c := 1 } lt(c, mload(add(challenges_ptr, mul(phase, 0x40)))) { c := add(c, 1) } { // We + challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) + } + } + + // Read evaluations + for + { let proof_cptr_end := add(proof_cptr, {{ (32 * num_evals)|hex() }}) } + lt(proof_cptr, proof_cptr_end) + {} + { + let eval := calldataload(proof_cptr) + success := and(success, lt(eval, r)) + mstore(hash_mptr, eval) + proof_cptr := add(proof_cptr, 0x20) + hash_mptr := add(hash_mptr, 0x20) + } + + // Read batch opening proof and generate challenges + {%- match scheme %} + {%- when Bdfg21 %} + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // zeta + challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) // nu + + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) // W + + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // mu + + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) // W' + {%- when Gwc19 %} + // TODO + {%- endmatch %} + + // Copy full vk into memory + extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + + // Read accumulator from instances + if mload(HAS_ACCUMULATOR_MPTR) { + let num_limbs := mload(NUM_ACC_LIMBS_MPTR) + let num_limb_bits := mload(NUM_ACC_LIMB_BITS_MPTR) + + let cptr := add(INSTANCE_CPTR, mul(mload(ACC_OFFSET_MPTR), 0x20)) + let lhs_y_off := mul(num_limbs, 0x20) + let rhs_x_off := mul(lhs_y_off, 2) + let rhs_y_off := mul(lhs_y_off, 3) + let lhs_x := calldataload(cptr) + let lhs_y := calldataload(add(cptr, lhs_y_off)) + let rhs_x := calldataload(add(cptr, rhs_x_off)) + let rhs_y := calldataload(add(cptr, rhs_y_off)) + for + { + let cptr_end := add(cptr, mul(0x20, num_limbs)) + let shift := num_limb_bits + } + lt(cptr, cptr_end) + {} + { + cptr := add(cptr, 0x20) + lhs_x := add(lhs_x, shl(shift, calldataload(cptr))) + lhs_y := add(lhs_y, shl(shift, calldataload(add(cptr, lhs_y_off)))) + rhs_x := add(rhs_x, shl(shift, calldataload(add(cptr, rhs_x_off)))) + rhs_y := add(rhs_y, shl(shift, calldataload(add(cptr, rhs_y_off)))) + shift := add(shift, num_limb_bits) + } + + // success := and(success, eq(mulmod(lhs_y, lhs_y, q), addmod(mulmod(lhs_x, mulmod(lhs_x, lhs_x, q), q), 3, q))) + // success := and(success, eq(mulmod(rhs_y, rhs_y, q), addmod(mulmod(rhs_x, mulmod(rhs_x, rhs_x, q), q), 3, q))) + + mstore(ACC_LHS_X_MPTR, lhs_x) + mstore(ACC_LHS_Y_MPTR, lhs_y) + mstore(ACC_RHS_X_MPTR, rhs_x) + mstore(ACC_RHS_Y_MPTR, rhs_y) + } + + pop(q) + } + + // Revert earlier if anything from calldata is invalid + if iszero(success) { + revert(0, 0) + } + + + // Compute lagrange evaluations and instance evaluation + { + let k := mload(K_MPTR) + let x := mload(X_MPTR) + let x_n := x + for + { let idx := 0 } + lt(idx, k) + { idx := add(idx, 1) } + { + x_n := mulmod(x_n, x_n, r) + } + + let omega := mload(OMEGA_MPTR) + + let mptr := X_N_MPTR + let mptr_end := add(mptr, mul(0x20, add(mload(NUM_INSTANCES_MPTR), {{ num_neg_lagranges }}))) + if iszero(mload(NUM_INSTANCES_MPTR)) { + mptr_end := add(mptr_end, 0x20) + } + for + { let pow_of_omega := mload(OMEGA_INV_TO_L_MPTR) } + lt(mptr, mptr_end) + { mptr := add(mptr, 0x20) } + { + mstore(mptr, addmod(x, sub(r, pow_of_omega), r)) + pow_of_omega := mulmod(pow_of_omega, omega, r) + } + let x_n_minus_1 := addmod(x_n, sub(r, 1), r) + mstore(mptr_end, x_n_minus_1) + success := batch_invert(success, X_N_MPTR, add(mptr_end, 0x20), r) + + mptr := X_N_MPTR + let l_i_common := mulmod(x_n_minus_1, mload(N_INV_MPTR), r) + for + { let pow_of_omega := mload(OMEGA_INV_TO_L_MPTR) } + lt(mptr, mptr_end) + { mptr := add(mptr, 0x20) } + { + mstore(mptr, mulmod(l_i_common, mulmod(mload(mptr), pow_of_omega, r), r)) + pow_of_omega := mulmod(pow_of_omega, omega, r) + } + + let l_blind := mload(add(X_N_MPTR, 0x20)) + let l_i_cptr := add(X_N_MPTR, 0x40) + for + { let l_i_cptr_end := add(X_N_MPTR, {{ (num_neg_lagranges * 32)|hex() }}) } + lt(l_i_cptr, l_i_cptr_end) + { l_i_cptr := add(l_i_cptr, 0x20) } + { + l_blind := addmod(l_blind, mload(l_i_cptr), r) + } + + let instance_eval := 0 + for + { + let instance_cptr := INSTANCE_CPTR + let instance_cptr_end := add(instance_cptr, mul(0x20, mload(NUM_INSTANCES_MPTR))) + } + lt(instance_cptr, instance_cptr_end) + { + instance_cptr := add(instance_cptr, 0x20) + l_i_cptr := add(l_i_cptr, 0x20) + } + { + instance_eval := addmod(instance_eval, mulmod(mload(l_i_cptr), calldataload(instance_cptr), r), r) + } + + let x_n_minus_1_inv := mload(mptr_end) + let l_last := mload(X_N_MPTR) + let l_0 := mload(add(X_N_MPTR, {{ (num_neg_lagranges * 32)|hex() }})) + + mstore(X_N_MPTR, x_n) + mstore(X_N_MINUS_1_INV_MPTR, x_n_minus_1_inv) + mstore(L_LAST_MPTR, l_last) + mstore(L_BLIND_MPTR, l_blind) + mstore(L_0_MPTR, l_0) + mstore(INSTANCE_EVAL_MPTR, instance_eval) + } + + // Compute quotient evavluation + { + let quotient_eval_numer + let delta := 4131629893567559867359510883348571134090853742863529169391034518566172092834 + let y := mload(Y_MPTR) + + {%- for code_block in quotient_eval_numer_computations %} + { + {%- for line in code_block %} + {{ line }} + {%- endfor %} + } + {%- endfor %} + + pop(y) + pop(delta) + + let quotient_eval := mulmod(quotient_eval_numer, mload(X_N_MINUS_1_INV_MPTR), r) + mstore(QUOTIENT_EVAL_MPTR, quotient_eval) + } + + // Compute quotient commitment + { + mstore(0x00, calldataload(LAST_QUOTIENT_X_CPTR)) + mstore(0x20, calldataload(add(LAST_QUOTIENT_X_CPTR, 0x20))) + let x_n := mload(X_N_MPTR) + for + { + let cptr := sub(LAST_QUOTIENT_X_CPTR, 0x40) + let cptr_end := sub(FIRST_QUOTIENT_X_CPTR, 0x40) + } + lt(cptr_end, cptr) + {} + { + success := ec_mul_acc(success, x_n) + success := ec_add_acc(success, calldataload(cptr), calldataload(add(cptr, 0x20))) + cptr := sub(cptr, 0x40) + } + mstore(QUOTIENT_X_MPTR, mload(0x00)) + mstore(QUOTIENT_Y_MPTR, mload(0x20)) + } + + // Compute pairing lhs and rhs + { + {%- for code_block in pcs_computations %} + { + {%- for line in code_block %} + {{ line }} + {%- endfor %} + } + {%- endfor %} + } + + // Random linear combine with accumulator + if mload(HAS_ACCUMULATOR_MPTR) { + mstore(0x00, mload(ACC_LHS_X_MPTR)) + mstore(0x20, mload(ACC_LHS_Y_MPTR)) + mstore(0x40, mload(ACC_RHS_X_MPTR)) + mstore(0x60, mload(ACC_RHS_Y_MPTR)) + mstore(0x80, mload(PAIRING_LHS_X_MPTR)) + mstore(0xa0, mload(PAIRING_LHS_Y_MPTR)) + mstore(0xc0, mload(PAIRING_RHS_X_MPTR)) + mstore(0xe0, mload(PAIRING_RHS_Y_MPTR)) + let challenge := mod(keccak256(0x00, 0x100), r) + + // [pairing_lhs] += challenge * [acc_lhs] + success := ec_mul_acc(success, challenge) + success := ec_add_acc(success, mload(PAIRING_LHS_X_MPTR), mload(PAIRING_LHS_Y_MPTR)) + mstore(PAIRING_LHS_X_MPTR, mload(0x00)) + mstore(PAIRING_LHS_Y_MPTR, mload(0x20)) + + // [pairing_rhs] += challenge * [acc_rhs] + mstore(0x00, mload(ACC_RHS_X_MPTR)) + mstore(0x20, mload(ACC_RHS_Y_MPTR)) + success := ec_mul_acc(success, challenge) + success := ec_add_acc(success, mload(PAIRING_RHS_X_MPTR), mload(PAIRING_RHS_Y_MPTR)) + mstore(PAIRING_RHS_X_MPTR, mload(0x00)) + mstore(PAIRING_RHS_Y_MPTR, mload(0x20)) + } + + // Perform pairing + success := ec_pairing( + success, + mload(PAIRING_LHS_X_MPTR), + mload(PAIRING_LHS_Y_MPTR), + mload(PAIRING_RHS_X_MPTR), + mload(PAIRING_RHS_Y_MPTR) + ) + + + // Revert if anything fails + if iszero(success) { + revert(0x00, 0x00) + } + + // Return 1 as result if everything succeeds + mstore(0x00, 1) + return(0x00, 0x20) + } + } +} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 8b0f856..eef7e4b 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -22,7 +22,14 @@ contract Halo2VerifyingKey { {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_lookup_input_expressions[{{ loop.index0 }}] {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len()))|hex() }}) + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len() %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // num_advices_user_challenges length + {%- for (x, y) in num_advices_user_challenges %} + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len() + 1 %} + mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // num_advices[{{ loop.index0 }}].x + mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 1))|hex() }}) } } } From 54c088d85189a290a47c5ff2eb1a692787fc3af2 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 1 Jul 2024 17:25:34 -0500 Subject: [PATCH 11/65] *fix clippy warnings and vk loading bug. --- src/codegen.rs | 4 ---- templates/Halo2VerifierReusable.sol | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 36d85e7..c10e72c 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -367,10 +367,6 @@ impl<'a> SolidityGenerator<'a> { let offset = vk_m + (vk.constants.len() * 0x20) + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; - let arr_mptr = vk_m - + (vk.constants.len() * 0x20) - + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40 - + (vk.const_lookup_input_expressions.len() * 0x20); // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. vk.const_lookup_input_expressions .iter() diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 2bc10d6..d78a3ee 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -220,8 +220,8 @@ contract Halo2Verifier { let success := true { - // Load vk_digest, num_instances and NUM_ADVICES_USER_CHALLENGES_OFFSET of vk into memory - extcodecopy(vk, VK_MPTR, 0x00, 0x60) + // Copy full vk into memory + extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) // Check valid length of proof success := and(success, eq({{ proof_len|hex() }}, calldataload(sub(PROOF_LEN_CPTR, 0x6014F51900)))) From 164fa25c9cfd74e52b39cc23f68cda4d577f75d8 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 1 Jul 2024 19:02:51 -0500 Subject: [PATCH 12/65] *cache proof_len_cptr in vk. --- src/codegen.rs | 18 +++++---- src/codegen/template.rs | 1 - templates/Halo2VerifierReusable.sol | 57 +++++++++++++++-------------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index c10e72c..d1f474c 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -231,8 +231,6 @@ impl<'a> SolidityGenerator<'a> { .tuples() .collect(); - let proof_cptr = Ptr::calldata(0x64); - let attached_vk = Halo2VerifyingKey { constants: constants.clone(), fixed_comms: fixed_comms.clone(), @@ -245,9 +243,10 @@ impl<'a> SolidityGenerator<'a> { return attached_vk; } - let vk_mptr = - Ptr::memory(self.estimate_static_working_memory_size(&attached_vk, proof_cptr)); - let data = Data::new(&self.meta, &attached_vk, vk_mptr, proof_cptr); + let vk_mptr = Ptr::memory( + self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x64)), + ); + let data = Data::new(&self.meta, &attached_vk, vk_mptr, Ptr::calldata(0x64)); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); @@ -257,6 +256,11 @@ impl<'a> SolidityGenerator<'a> { let const_lookup_input_expressions = result.1.into_iter().map(fr_to_u256).collect::>(); + let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); + + // insert it at position 3 of the constants. + constants.insert(2, ("instance_cptr", instance_cptr)); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_lookup_input_expressions.len() * 0x20) @@ -355,8 +359,6 @@ impl<'a> SolidityGenerator<'a> { fn generate_separate_verifier(&self) -> Halo2VerifierReusable { let proof_cptr = Ptr::calldata(0x84); - let proof_len_cptr = Ptr::calldata(0x6014F51964); - let vk = self.generate_vk(true); let vk_len = vk.len(); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); @@ -412,7 +414,7 @@ impl<'a> SolidityGenerator<'a> { num_evals: self.meta.num_evals, num_quotients: self.meta.num_quotients, proof_cptr, - proof_len_cptr, + // proof_len_cptr, quotient_comm_cptr: data.quotient_comm_cptr, proof_len: self.meta.proof_len(self.scheme), challenge_mptr: data.challenge_mptr, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 1d8b027..4e1326a 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -56,7 +56,6 @@ pub(crate) struct Halo2VerifierReusable { pub(crate) challenge_mptr: Ptr, pub(crate) theta_mptr: Ptr, pub(crate) proof_cptr: Ptr, - pub(crate) proof_len_cptr: Ptr, pub(crate) quotient_comm_cptr: Ptr, pub(crate) num_neg_lagranges: usize, pub(crate) num_evals: usize, diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index d78a3ee..0c4c607 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -3,10 +3,8 @@ pragma solidity ^0.8.0; contract Halo2Verifier { - uint256 internal constant PROOF_LEN_CPTR = {{ proof_len_cptr }}; - uint256 internal constant PROOF_CPTR = {{ proof_cptr }}; - uint256 internal constant NUM_INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) }}; - uint256 internal constant INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) + 1 }}; + uint256 internal constant PROOF_LEN_CPTR = 0x64; + uint256 internal constant PROOF_CPTR = 0x84; uint256 internal constant FIRST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr }}; uint256 internal constant LAST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr + 2 * (num_quotients - 1) }}; @@ -15,25 +13,26 @@ contract Halo2Verifier { uint256 internal constant VK_DIGEST_MPTR = {{ vk_mptr }}; uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 1 }}; uint256 internal constant NUM_ADVICES_USER_CHALLENGES_OFFSET = {{ vk_mptr + 2 }}; - uint256 internal constant K_MPTR = {{ vk_mptr + 3 }}; - uint256 internal constant N_INV_MPTR = {{ vk_mptr + 4 }}; - uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 5 }}; - uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 6 }}; - uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 7 }}; - uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 8 }}; - uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 9 }}; - uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 10 }}; - uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 11 }}; - uint256 internal constant G1_X_MPTR = {{ vk_mptr + 12 }}; - uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 13 }}; - uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 14 }}; - uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 15 }}; - uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 16 }}; - uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 17 }}; - uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 18 }}; - uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 19 }}; - uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 20 }}; - uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 21 }}; + uint256 internal constant INSTANCE_CPTR = {{ vk_mptr + 3 }}; + uint256 internal constant K_MPTR = {{ vk_mptr + 4 }}; + uint256 internal constant N_INV_MPTR = {{ vk_mptr + 5 }}; + uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 6 }}; + uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 7 }}; + uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 8 }}; + uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 9 }}; + uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 10 }}; + uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 11 }}; + uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 12 }}; + uint256 internal constant G1_X_MPTR = {{ vk_mptr + 13 }}; + uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 14 }}; + uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 15 }}; + uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 16 }}; + uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 17 }}; + uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 18 }}; + uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 19 }}; + uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 20 }}; + uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 21 }}; + uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 22 }}; uint256 internal constant CHALLENGE_MPTR = {{ challenge_mptr }}; @@ -222,19 +221,21 @@ contract Halo2Verifier { { // Copy full vk into memory extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + + let instance_cptr := mload(INSTANCE_CPTR) + // Check valid length of proof - success := and(success, eq({{ proof_len|hex() }}, calldataload(sub(PROOF_LEN_CPTR, 0x6014F51900)))) + success := and(success, eq(sub(instance_cptr, 0xa4), calldataload(PROOF_LEN_CPTR))) // Check valid length of instances let num_instances := mload(NUM_INSTANCES_MPTR) - success := and(success, eq(num_instances, calldataload(NUM_INSTANCE_CPTR))) + success := and(success, eq(num_instances, calldataload(sub(instance_cptr,0x20)))) // Absorb vk diegst mstore(0x00, mload(VK_DIGEST_MPTR)) // Read instances and witness commitments and generate challenges let hash_mptr := 0x20 - let instance_cptr := INSTANCE_CPTR for { let instance_cptr_end := add(instance_cptr, mul(0x20, num_instances)) } lt(instance_cptr, instance_cptr_end) @@ -309,7 +310,7 @@ contract Halo2Verifier { let num_limbs := mload(NUM_ACC_LIMBS_MPTR) let num_limb_bits := mload(NUM_ACC_LIMB_BITS_MPTR) - let cptr := add(INSTANCE_CPTR, mul(mload(ACC_OFFSET_MPTR), 0x20)) + let cptr := add(mload(INSTANCE_CPTR), mul(mload(ACC_OFFSET_MPTR), 0x20)) let lhs_y_off := mul(num_limbs, 0x20) let rhs_x_off := mul(lhs_y_off, 2) let rhs_y_off := mul(lhs_y_off, 3) @@ -407,7 +408,7 @@ contract Halo2Verifier { let instance_eval := 0 for { - let instance_cptr := INSTANCE_CPTR + let instance_cptr := mload(INSTANCE_CPTR) let instance_cptr_end := add(instance_cptr, mul(0x20, mload(NUM_INSTANCES_MPTR))) } lt(instance_cptr, instance_cptr_end) From aea26a618b7c3119fcc00edb782d260527db34b9 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 2 Jul 2024 17:30:59 -0500 Subject: [PATCH 13/65] *cache quotient x's --- src/codegen.rs | 30 ++++++++++++---- src/codegen/template.rs | 4 --- templates/Halo2VerifierReusable.sol | 53 ++++++++++++++--------------- 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index d1f474c..0beb714 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -244,9 +244,9 @@ impl<'a> SolidityGenerator<'a> { } let vk_mptr = Ptr::memory( - self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x64)), + self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), ); - let data = Data::new(&self.meta, &attached_vk, vk_mptr, Ptr::calldata(0x64)); + let data = Data::new(&self.meta, &attached_vk, vk_mptr, Ptr::calldata(0x84)); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); @@ -261,6 +261,27 @@ impl<'a> SolidityGenerator<'a> { // insert it at position 3 of the constants. constants.insert(2, ("instance_cptr", instance_cptr)); + let first_quotient_x_cptr = data.quotient_comm_cptr; + + constants.insert( + 2, + ( + "first_quotient_x_cptr", + U256::from(first_quotient_x_cptr.value().as_usize()), + ), + ); + + let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); + println!("num_quotients: {}", self.meta.num_quotients); + + constants.insert( + 2, + ( + "last_quotient_x_cptr", + U256::from(last_quotient_x_cptr.value().as_usize()), + ), + ); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_lookup_input_expressions.len() * 0x20) @@ -412,11 +433,6 @@ impl<'a> SolidityGenerator<'a> { vk_mptr, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, num_evals: self.meta.num_evals, - num_quotients: self.meta.num_quotients, - proof_cptr, - // proof_len_cptr, - quotient_comm_cptr: data.quotient_comm_cptr, - proof_len: self.meta.proof_len(self.scheme), challenge_mptr: data.challenge_mptr, theta_mptr: data.theta_mptr, quotient_eval_numer_computations, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 4e1326a..2d04a7e 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -51,15 +51,11 @@ pub(crate) struct Halo2Verifier { pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, pub(crate) vk_len: usize, - pub(crate) proof_len: usize, pub(crate) vk_mptr: Ptr, pub(crate) challenge_mptr: Ptr, pub(crate) theta_mptr: Ptr, - pub(crate) proof_cptr: Ptr, - pub(crate) quotient_comm_cptr: Ptr, pub(crate) num_neg_lagranges: usize, pub(crate) num_evals: usize, - pub(crate) num_quotients: usize, pub(crate) quotient_eval_numer_computations: Vec>, pub(crate) pcs_computations: Vec>, } diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 0c4c607..ecdafc8 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -6,33 +6,32 @@ contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; uint256 internal constant PROOF_CPTR = 0x84; - uint256 internal constant FIRST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr }}; - uint256 internal constant LAST_QUOTIENT_X_CPTR = {{ quotient_comm_cptr + 2 * (num_quotients - 1) }}; - uint256 internal constant VK_MPTR = {{ vk_mptr }}; uint256 internal constant VK_DIGEST_MPTR = {{ vk_mptr }}; uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 1 }}; uint256 internal constant NUM_ADVICES_USER_CHALLENGES_OFFSET = {{ vk_mptr + 2 }}; - uint256 internal constant INSTANCE_CPTR = {{ vk_mptr + 3 }}; - uint256 internal constant K_MPTR = {{ vk_mptr + 4 }}; - uint256 internal constant N_INV_MPTR = {{ vk_mptr + 5 }}; - uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 6 }}; - uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 7 }}; - uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 8 }}; - uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 9 }}; - uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 10 }}; - uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 11 }}; - uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 12 }}; - uint256 internal constant G1_X_MPTR = {{ vk_mptr + 13 }}; - uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 14 }}; - uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 15 }}; - uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 16 }}; - uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 17 }}; - uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 18 }}; - uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 19 }}; - uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 20 }}; - uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 21 }}; - uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 22 }}; + uint256 internal constant LAST_QUOTIENT_X_CPTR = {{ vk_mptr + 3 }}; + uint256 internal constant FIRST_QUOTIENT_X_CPTR = {{ vk_mptr + 4 }}; + uint256 internal constant INSTANCE_CPTR = {{ vk_mptr + 5 }}; + uint256 internal constant K_MPTR = {{ vk_mptr + 6 }}; + uint256 internal constant N_INV_MPTR = {{ vk_mptr + 7 }}; + uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 8 }}; + uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 9 }}; + uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 10 }}; + uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 11 }}; + uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 12 }}; + uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 13 }}; + uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 14 }}; + uint256 internal constant G1_X_MPTR = {{ vk_mptr + 15 }}; + uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 16 }}; + uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 17 }}; + uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 18 }}; + uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 19 }}; + uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 20 }}; + uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 21 }}; + uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 22 }}; + uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 23 }}; + uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 24 }}; uint256 internal constant CHALLENGE_MPTR = {{ challenge_mptr }}; @@ -455,13 +454,13 @@ contract Halo2Verifier { // Compute quotient commitment { - mstore(0x00, calldataload(LAST_QUOTIENT_X_CPTR)) - mstore(0x20, calldataload(add(LAST_QUOTIENT_X_CPTR, 0x20))) + mstore(0x00, calldataload(mload(LAST_QUOTIENT_X_CPTR))) + mstore(0x20, calldataload(add(mload(LAST_QUOTIENT_X_CPTR), 0x20))) let x_n := mload(X_N_MPTR) for { - let cptr := sub(LAST_QUOTIENT_X_CPTR, 0x40) - let cptr_end := sub(FIRST_QUOTIENT_X_CPTR, 0x40) + let cptr := sub(mload(LAST_QUOTIENT_X_CPTR), 0x40) + let cptr_end := sub(mload(FIRST_QUOTIENT_X_CPTR), 0x40) } lt(cptr_end, cptr) {} From 25f27aba5145fd4ce24de7d28e422e4bf9cf70af Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 2 Jul 2024 20:57:29 -0500 Subject: [PATCH 14/65] *cache all vk_ptr offset solidity constants in vk. --- src/codegen.rs | 37 +++++++--- src/codegen/pcs.rs | 38 ++++++++-- src/codegen/template.rs | 2 - templates/Halo2VerifierReusable.sol | 103 +++++++++++----------------- 4 files changed, 99 insertions(+), 81 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 0beb714..26c84bb 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -243,10 +243,10 @@ impl<'a> SolidityGenerator<'a> { return attached_vk; } - let vk_mptr = Ptr::memory( + let vk_mptr_mock = Ptr::memory( self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), ); - let data = Data::new(&self.meta, &attached_vk, vk_mptr, Ptr::calldata(0x84)); + let data = Data::new(&self.meta, &attached_vk, vk_mptr_mock, Ptr::calldata(0x84)); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); @@ -272,7 +272,6 @@ impl<'a> SolidityGenerator<'a> { ); let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); - println!("num_quotients: {}", self.meta.num_quotients); constants.insert( 2, @@ -282,6 +281,15 @@ impl<'a> SolidityGenerator<'a> { ), ); + // insert mock vk_mptr_mock at position 0 + constants.insert(1, ("vk_mptr", U256::from(vk_mptr_mock.value().as_usize()))); + + // insert mock vk_len at position 1 + + let vk_len_mock = attached_vk.len(); + + constants.insert(2, ("vk_len", U256::from(vk_len_mock))); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_lookup_input_expressions.len() * 0x20) @@ -289,7 +297,7 @@ impl<'a> SolidityGenerator<'a> { // insert it at position 3 of the constants. constants.insert( - 2, + 4, ( "num_advices_user_challenges_offset", U256::from(num_advices_user_challenges_offset), @@ -313,13 +321,23 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); - Halo2VerifyingKey { + let mut vk = Halo2VerifyingKey { constants, fixed_comms, permutation_comms, const_lookup_input_expressions, num_advices_user_challenges, - } + }; + // new generate the real vk_mptr + let vk_mptr = Ptr::memory( + self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), + ); + // replace the mock vk_mptr with the real vk_mptr + vk.constants[1] = ("vk_mptr", U256::from(vk_mptr.value().as_usize())); + // replace the mock vk_len with the real vk_len + let vk_len = vk.len(); + vk.constants[2] = ("vk_len", U256::from(vk_len)); + vk } fn generate_verifier(&self) -> Halo2Verifier { @@ -353,7 +371,7 @@ impl<'a> SolidityGenerator<'a> { .collect(); let pcs_computations = match self.scheme { - Bdfg21 => bdfg21_computations(&self.meta, &data), + Bdfg21 => bdfg21_computations(&self.meta, &data, false), Gwc19 => unimplemented!(), }; @@ -381,7 +399,6 @@ impl<'a> SolidityGenerator<'a> { let proof_cptr = Ptr::calldata(0x84); let vk = self.generate_vk(true); - let vk_len = vk.len(); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); let vk_mptr = Ptr::memory(vk_m); // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. @@ -423,14 +440,12 @@ impl<'a> SolidityGenerator<'a> { .collect(); let pcs_computations = match self.scheme { - Bdfg21 => bdfg21_computations(&self.meta, &data), + Bdfg21 => bdfg21_computations(&self.meta, &data, true), Gwc19 => unimplemented!(), }; Halo2VerifierReusable { scheme: self.scheme, - vk_len, - vk_mptr, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, num_evals: self.meta.num_evals, challenge_mptr: data.challenge_mptr, diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index e00895c..e37c042 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -203,7 +203,11 @@ pub(crate) fn rotation_sets(queries: &[Query]) -> (BTreeSet, Vec Vec> { +pub(crate) fn bdfg21_computations( + meta: &ConstraintSystemMeta, + data: &Data, + separate: bool, +) -> Vec> { let queries = queries(meta, data); let (superset, sets) = rotation_sets(&queries); let min_rot = *superset.first().unwrap(); @@ -244,11 +248,35 @@ pub(crate) fn bdfg21_computations(meta: &ConstraintSystemMeta, data: &Data) -> V let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); + // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. + // Otherwise we load in omega and omega_inv from the solidity constants. + + let omega = if separate { + "add(vk_mptr, 0x140)" + } else { + "OMEGA_MPTR" + }; + let omega_inv = if separate { + "add(vk_mptr, 0x160)" + } else { + "OMEGA_INV_MPTR" + }; + + let g1_x = if separate { + "add(vk_mptr, 0x0220)" + } else { + "G1_X_MPTR" + }; + let g1_y = if separate { + "add(vk_mptr, 0x0240)" + } else { + "G1_Y_MPTR" + }; let point_computations = chain![ [ "let x := mload(X_MPTR)", - "let omega := mload(OMEGA_MPTR)", - "let omega_inv := mload(OMEGA_INV_MPTR)", + format!("let omega := mload({})", omega).as_str(), + format!("let omega_inv := mload({})", omega_inv).as_str(), "let x_pow_of_omega := mulmod(x, omega, r)" ] .map(str::to_string), @@ -606,8 +634,8 @@ pub(crate) fn bdfg21_computations(meta: &ConstraintSystemMeta, data: &Data) -> V .collect_vec() }), [ - format!("mstore(0x80, mload(G1_X_MPTR))"), - format!("mstore(0xa0, mload(G1_Y_MPTR))"), + format!("mstore(0x80, mload({}))", g1_x), + format!("mstore(0xa0, mload({}))", g1_y), format!("success := ec_mul_tmp(success, sub(r, mload(R_EVAL_MPTR)))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w.x()), diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 2d04a7e..b0f3130 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -50,8 +50,6 @@ pub(crate) struct Halo2Verifier { #[template(path = "Halo2VerifierReusable.sol")] pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, - pub(crate) vk_len: usize, - pub(crate) vk_mptr: Ptr, pub(crate) challenge_mptr: Ptr, pub(crate) theta_mptr: Ptr, pub(crate) num_neg_lagranges: usize, diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index ecdafc8..c6447a8 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -6,33 +6,6 @@ contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; uint256 internal constant PROOF_CPTR = 0x84; - uint256 internal constant VK_MPTR = {{ vk_mptr }}; - uint256 internal constant VK_DIGEST_MPTR = {{ vk_mptr }}; - uint256 internal constant NUM_INSTANCES_MPTR = {{ vk_mptr + 1 }}; - uint256 internal constant NUM_ADVICES_USER_CHALLENGES_OFFSET = {{ vk_mptr + 2 }}; - uint256 internal constant LAST_QUOTIENT_X_CPTR = {{ vk_mptr + 3 }}; - uint256 internal constant FIRST_QUOTIENT_X_CPTR = {{ vk_mptr + 4 }}; - uint256 internal constant INSTANCE_CPTR = {{ vk_mptr + 5 }}; - uint256 internal constant K_MPTR = {{ vk_mptr + 6 }}; - uint256 internal constant N_INV_MPTR = {{ vk_mptr + 7 }}; - uint256 internal constant OMEGA_MPTR = {{ vk_mptr + 8 }}; - uint256 internal constant OMEGA_INV_MPTR = {{ vk_mptr + 9 }}; - uint256 internal constant OMEGA_INV_TO_L_MPTR = {{ vk_mptr + 10 }}; - uint256 internal constant HAS_ACCUMULATOR_MPTR = {{ vk_mptr + 11 }}; - uint256 internal constant ACC_OFFSET_MPTR = {{ vk_mptr + 12 }}; - uint256 internal constant NUM_ACC_LIMBS_MPTR = {{ vk_mptr + 13 }}; - uint256 internal constant NUM_ACC_LIMB_BITS_MPTR = {{ vk_mptr + 14 }}; - uint256 internal constant G1_X_MPTR = {{ vk_mptr + 15 }}; - uint256 internal constant G1_Y_MPTR = {{ vk_mptr + 16 }}; - uint256 internal constant G2_X_1_MPTR = {{ vk_mptr + 17 }}; - uint256 internal constant G2_X_2_MPTR = {{ vk_mptr + 18 }}; - uint256 internal constant G2_Y_1_MPTR = {{ vk_mptr + 19 }}; - uint256 internal constant G2_Y_2_MPTR = {{ vk_mptr + 20 }}; - uint256 internal constant NEG_S_G2_X_1_MPTR = {{ vk_mptr + 21 }}; - uint256 internal constant NEG_S_G2_X_2_MPTR = {{ vk_mptr + 22 }}; - uint256 internal constant NEG_S_G2_Y_1_MPTR = {{ vk_mptr + 23 }}; - uint256 internal constant NEG_S_G2_Y_2_MPTR = {{ vk_mptr + 24 }}; - uint256 internal constant CHALLENGE_MPTR = {{ challenge_mptr }}; uint256 internal constant THETA_MPTR = {{ theta_mptr }}; @@ -193,19 +166,19 @@ contract Halo2Verifier { // Perform pairing check. // Return updated (success). - function ec_pairing(success, lhs_x, lhs_y, rhs_x, rhs_y) -> ret { + function ec_pairing(success, vk_mptr, lhs_x, lhs_y, rhs_x, rhs_y) -> ret { mstore(0x00, lhs_x) mstore(0x20, lhs_y) - mstore(0x40, mload(G2_X_1_MPTR)) - mstore(0x60, mload(G2_X_2_MPTR)) - mstore(0x80, mload(G2_Y_1_MPTR)) - mstore(0xa0, mload(G2_Y_2_MPTR)) + mstore(0x40, mload(add(vk_mptr, 0x0260))) + mstore(0x60, mload(add(vk_mptr, 0x0280))) + mstore(0x80, mload(add(vk_mptr, 0x02a0))) + mstore(0xa0, mload(add(vk_mptr, 0x02c0))) mstore(0xc0, rhs_x) mstore(0xe0, rhs_y) - mstore(0x100, mload(NEG_S_G2_X_1_MPTR)) - mstore(0x120, mload(NEG_S_G2_X_2_MPTR)) - mstore(0x140, mload(NEG_S_G2_Y_1_MPTR)) - mstore(0x160, mload(NEG_S_G2_Y_2_MPTR)) + mstore(0x100, mload(add(vk_mptr, 0x02e0))) + mstore(0x120, mload(add(vk_mptr, 0x0300))) + mstore(0x140, mload(add(vk_mptr, 0x0320))) + mstore(0x160, mload(add(vk_mptr, 0x0340))) ret := and(success, staticcall(gas(), 0x08, 0x00, 0x180, 0x00, 0x20)) ret := and(ret, mload(0x00)) } @@ -216,23 +189,25 @@ contract Halo2Verifier { // Initialize success as true let success := true - + // Initialize vk_mptr as 0x0 + let vk_mptr := 0x0 { + // Load in the vk_digest, vk_mptr and vk_len + extcodecopy(vk, 0x0, 0x00, 0x60) + vk_mptr := mload(0x20) + let vk_len := mload(0x40) // Copy full vk into memory - extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + extcodecopy(vk, vk_mptr, 0x00, vk_len) - let instance_cptr := mload(INSTANCE_CPTR) + let instance_cptr := mload(add(vk_mptr, 0xe0)) // Check valid length of proof success := and(success, eq(sub(instance_cptr, 0xa4), calldataload(PROOF_LEN_CPTR))) // Check valid length of instances - let num_instances := mload(NUM_INSTANCES_MPTR) + let num_instances := mload(add(vk_mptr,0x60)) success := and(success, eq(num_instances, calldataload(sub(instance_cptr,0x20)))) - // Absorb vk diegst - mstore(0x00, mload(VK_DIGEST_MPTR)) - // Read instances and witness commitments and generate challenges let hash_mptr := 0x20 for @@ -249,7 +224,7 @@ contract Halo2Verifier { let proof_cptr := PROOF_CPTR let challenge_mptr := CHALLENGE_MPTR - let num_advices_ptr := add(VK_MPTR, mload(NUM_ADVICES_USER_CHALLENGES_OFFSET)) + let num_advices_ptr := add(vk_mptr, mload(add(vk_mptr, 0x80))) let num_advices_len := mload(num_advices_ptr) let advices_ptr := add(num_advices_ptr, 0x20) // start of advices let challenges_ptr := add(advices_ptr, 0x20) // start of challenges @@ -302,14 +277,14 @@ contract Halo2Verifier { {%- endmatch %} // Copy full vk into memory - extcodecopy(vk, VK_MPTR, 0x00, {{ vk_len|hex() }}) + extcodecopy(vk, vk_mptr, 0x00, vk_len) // Read accumulator from instances - if mload(HAS_ACCUMULATOR_MPTR) { - let num_limbs := mload(NUM_ACC_LIMBS_MPTR) - let num_limb_bits := mload(NUM_ACC_LIMB_BITS_MPTR) + if mload(add(vk_mptr, 0x01a0)) { + let num_limbs := mload(add(vk_mptr, 0x01e0)) + let num_limb_bits := mload(add(vk_mptr, 0x0200)) - let cptr := add(mload(INSTANCE_CPTR), mul(mload(ACC_OFFSET_MPTR), 0x20)) + let cptr := add(mload(add(vk_mptr, 0xe0)), mul(mload(add(vk_mptr, 0x01c0)), 0x20)) let lhs_y_off := mul(num_limbs, 0x20) let rhs_x_off := mul(lhs_y_off, 2) let rhs_y_off := mul(lhs_y_off, 3) @@ -353,7 +328,7 @@ contract Halo2Verifier { // Compute lagrange evaluations and instance evaluation { - let k := mload(K_MPTR) + let k := mload(add(vk_mptr, 0x100)) let x := mload(X_MPTR) let x_n := x for @@ -364,15 +339,16 @@ contract Halo2Verifier { x_n := mulmod(x_n, x_n, r) } - let omega := mload(OMEGA_MPTR) + let omega := mload(add(vk_mptr, 0x140)) let mptr := X_N_MPTR - let mptr_end := add(mptr, mul(0x20, add(mload(NUM_INSTANCES_MPTR), {{ num_neg_lagranges }}))) - if iszero(mload(NUM_INSTANCES_MPTR)) { + let num_instances := mload(add(vk_mptr,0x60)) + let mptr_end := add(mptr, mul(0x20, add(num_instances, {{ num_neg_lagranges }}))) + if iszero(num_instances) { mptr_end := add(mptr_end, 0x20) } for - { let pow_of_omega := mload(OMEGA_INV_TO_L_MPTR) } + { let pow_of_omega := mload(add(vk_mptr, 0x180)) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { @@ -384,9 +360,9 @@ contract Halo2Verifier { success := batch_invert(success, X_N_MPTR, add(mptr_end, 0x20), r) mptr := X_N_MPTR - let l_i_common := mulmod(x_n_minus_1, mload(N_INV_MPTR), r) + let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)), r) for - { let pow_of_omega := mload(OMEGA_INV_TO_L_MPTR) } + { let pow_of_omega := mload(add(vk_mptr, 0x180)) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { @@ -407,8 +383,8 @@ contract Halo2Verifier { let instance_eval := 0 for { - let instance_cptr := mload(INSTANCE_CPTR) - let instance_cptr_end := add(instance_cptr, mul(0x20, mload(NUM_INSTANCES_MPTR))) + let instance_cptr := mload(add(vk_mptr, 0xe0)) + let instance_cptr_end := add(instance_cptr, mul(0x20, num_instances)) } lt(instance_cptr, instance_cptr_end) { @@ -454,13 +430,13 @@ contract Halo2Verifier { // Compute quotient commitment { - mstore(0x00, calldataload(mload(LAST_QUOTIENT_X_CPTR))) - mstore(0x20, calldataload(add(mload(LAST_QUOTIENT_X_CPTR), 0x20))) + mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) + mstore(0x20, calldataload(add(mload(add(vk_mptr, 0xa0)), 0x20))) let x_n := mload(X_N_MPTR) for { - let cptr := sub(mload(LAST_QUOTIENT_X_CPTR), 0x40) - let cptr_end := sub(mload(FIRST_QUOTIENT_X_CPTR), 0x40) + let cptr := sub(mload(add(vk_mptr, 0xa0)), 0x40) + let cptr_end := sub(mload(add(vk_mptr, 0xc0)), 0x40) } lt(cptr_end, cptr) {} @@ -485,7 +461,7 @@ contract Halo2Verifier { } // Random linear combine with accumulator - if mload(HAS_ACCUMULATOR_MPTR) { + if mload(add(vk_mptr, 0x01a0)) { mstore(0x00, mload(ACC_LHS_X_MPTR)) mstore(0x20, mload(ACC_LHS_Y_MPTR)) mstore(0x40, mload(ACC_RHS_X_MPTR)) @@ -514,6 +490,7 @@ contract Halo2Verifier { // Perform pairing success := ec_pairing( success, + vk_mptr, mload(PAIRING_LHS_X_MPTR), mload(PAIRING_LHS_Y_MPTR), mload(PAIRING_RHS_X_MPTR), From f5c3dbc7fcb562d51fbac8dfff6eb54549dda5d3 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 3 Jul 2024 00:39:31 -0500 Subject: [PATCH 15/65] *half way caching theta vars --- src/codegen.rs | 12 +++-- src/codegen/evaluator.rs | 15 +++++- src/codegen/pcs.rs | 8 ++- templates/Halo2VerifierReusable.sol | 81 +++++++++++++++-------------- 4 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 26c84bb..b25f72e 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -198,6 +198,9 @@ impl<'a> SolidityGenerator<'a> { let g2 = g2_to_u256s(self.params.g2()); let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); + let challenges_length = self.meta.challenge_indices.len(); + let challenges_length = U256::from(challenges_length); + vec![ ("vk_digest", vk_digest), ("num_instances", num_instances), @@ -220,6 +223,7 @@ impl<'a> SolidityGenerator<'a> { ("neg_s_g2_x_2", neg_s_g2[1]), ("neg_s_g2_y_1", neg_s_g2[2]), ("neg_s_g2_y_2", neg_s_g2[3]), + ("challenges_length", challenges_length), ] }; let fixed_comms: Vec<(U256, U256)> = chain![self.vk.fixed_commitments()] @@ -251,7 +255,7 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let result: (Vec<(Vec, String)>, Vec) = - evaluator.lookup_computations(None); + evaluator.lookup_computations(None, false); let const_lookup_input_expressions = result.1.into_iter().map(fr_to_u256).collect::>(); @@ -354,7 +358,7 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations(None).0 + evaluator.lookup_computations(None, false).0 ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -423,7 +427,9 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), evaluator.permutation_computations(), - evaluator.lookup_computations(Some(vk_lookup_const_table)).0 + evaluator + .lookup_computations(Some(vk_lookup_const_table), true) + .0 ] .enumerate() .map(|(idx, (mut lines, var))| { diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index d34b20d..d083734 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -125,6 +125,7 @@ where pub fn lookup_computations( &self, vk_lookup_const_table: Option, super::util::Ptr>>, + separate: bool, ) -> (Vec<(Vec, String)>, Vec) { let evaluate = |expressions: &Vec<_>| { // println!("expressions: {:?}", expressions); @@ -185,6 +186,15 @@ where let num_inputs = inputs.len(); let (table_0, rest_tables) = tables.split_first().unwrap(); let (phi, phi_next, m) = evals; + // if separate then use the theta_mptr on set on the stack + // otherwise use the solidity constant + let theta = if separate { "theta_mptr" } else { "THETA_MPTR" }; + // For all the the other pointers offset from the theta_mptr perfrom relavant add operation + let beta = if separate { + "add(theta_mptr, 0x20)" + } else { + "BETA_MPTR" + }; // print line the input tables [ vec![ @@ -197,8 +207,8 @@ where ], chain![ [ - "let theta := mload(THETA_MPTR)", - "let beta := mload(BETA_MPTR)", + format!("let theta := mload({})", theta).as_str(), + format!("let beta := mload({})", beta).as_str(), "let table" ] .map(str::to_string), @@ -308,6 +318,7 @@ where pub fn lookup_computations( &self, _vk_lookup_const_table: Option, super::util::Ptr>>, + _separate: bool, ) -> (Vec<(Vec, String)>, Vec) { let _ = |expressions: &Vec<_>, constants: &mut Vec| { expressions.iter().for_each(|expression| { diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index e37c042..015b966 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -272,9 +272,15 @@ pub(crate) fn bdfg21_computations( } else { "G1_Y_MPTR" }; + + let x_mptr = if separate { + "add(theta_mptr, 0x80)" + } else { + "X_MPTR" + }; let point_computations = chain![ [ - "let x := mload(X_MPTR)", + format!("let x := mload({})", x_mptr).as_str(), format!("let omega := mload({})", omega).as_str(), format!("let omega_inv := mload({})", omega_inv).as_str(), "let x_pow_of_omega := mulmod(x, omega, r)" diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index c6447a8..1b9410a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -5,9 +5,9 @@ pragma solidity ^0.8.0; contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; uint256 internal constant PROOF_CPTR = 0x84; + uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - uint256 internal constant CHALLENGE_MPTR = {{ challenge_mptr }}; - + // TODO finish offseting the memory pointers in the permutation columns method uint256 internal constant THETA_MPTR = {{ theta_mptr }}; uint256 internal constant BETA_MPTR = {{ theta_mptr + 1 }}; uint256 internal constant GAMMA_MPTR = {{ theta_mptr + 2 }}; @@ -28,7 +28,7 @@ contract Halo2Verifier { uint256 internal constant ACC_RHS_Y_MPTR = {{ theta_mptr + 11 }}; uint256 internal constant X_N_MPTR = {{ theta_mptr + 12 }}; uint256 internal constant X_N_MINUS_1_INV_MPTR = {{ theta_mptr + 13 }}; - uint256 internal constant L_LAST_MPTR = {{ theta_mptr + 14 }}; + uint256 internal constant L_LAST_MPTR = {{ theta_mptr + 14 }}; // X NEEDED from here on uint256 internal constant L_BLIND_MPTR = {{ theta_mptr + 15 }}; uint256 internal constant L_0_MPTR = {{ theta_mptr + 16 }}; uint256 internal constant INSTANCE_EVAL_MPTR = {{ theta_mptr + 17 }}; @@ -51,12 +51,12 @@ contract Halo2Verifier { // and check if the point is on affine plane, // and store them in (hash_mptr, hash_mptr + 0x20). // Return updated (success, proof_cptr, hash_mptr). - function read_ec_point(success, proof_cptr, hash_mptr, q) -> ret0, ret1, ret2 { + function read_ec_point(success, proof_cptr, hash_mptr) -> ret0, ret1, ret2 { let x := calldataload(proof_cptr) let y := calldataload(add(proof_cptr, 0x20)) - ret0 := and(success, lt(x, q)) - ret0 := and(ret0, lt(y, q)) - ret0 := and(ret0, eq(mulmod(y, y, q), addmod(mulmod(x, mulmod(x, x, q), q), 3, q))) + ret0 := and(success, lt(x, Q)) + ret0 := and(ret0, lt(y, Q)) + ret0 := and(ret0, eq(mulmod(y, y, Q), addmod(mulmod(x, mulmod(x, x, Q), Q), 3, Q))) mstore(hash_mptr, x) mstore(add(hash_mptr, 0x20), y) ret1 := add(proof_cptr, 0x40) @@ -184,16 +184,18 @@ contract Halo2Verifier { } // Modulus - let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // BN254 base field let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field // Initialize success as true let success := true - // Initialize vk_mptr as 0x0 + // Initialize vk_mptr as 0x0 on the stack let vk_mptr := 0x0 + // Initialize theta_mptr as 0x0 on the stack + let theta_mptr := 0x0 { // Load in the vk_digest, vk_mptr and vk_len extcodecopy(vk, 0x0, 0x00, 0x60) + // Set the vk_mptr vk_mptr := mload(0x20) let vk_len := mload(0x40) // Copy full vk into memory @@ -223,7 +225,9 @@ contract Halo2Verifier { } let proof_cptr := PROOF_CPTR - let challenge_mptr := CHALLENGE_MPTR + let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory + // Set the theta_mptr (vk_mptr + vk_len + challenges_length) + theta_mptr := add(challenge_mptr, mload(add(vk_mptr, 0x0360))) let num_advices_ptr := add(vk_mptr, mload(add(vk_mptr, 0x80))) let num_advices_len := mload(num_advices_ptr) let advices_ptr := add(num_advices_ptr, 0x20) // start of advices @@ -236,7 +240,7 @@ contract Halo2Verifier { // Phase loop for { } lt(proof_cptr, proof_cptr_end) { } { - success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) } // Generate challenges @@ -267,11 +271,11 @@ contract Halo2Verifier { challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // zeta challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) // nu - success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) // W + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) // W challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // mu - success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr, q) // W' + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) // W' {%- when Gwc19 %} // TODO {%- endmatch %} @@ -308,16 +312,15 @@ contract Halo2Verifier { shift := add(shift, num_limb_bits) } - // success := and(success, eq(mulmod(lhs_y, lhs_y, q), addmod(mulmod(lhs_x, mulmod(lhs_x, lhs_x, q), q), 3, q))) - // success := and(success, eq(mulmod(rhs_y, rhs_y, q), addmod(mulmod(rhs_x, mulmod(rhs_x, rhs_x, q), q), 3, q))) + success := and(success, eq(mulmod(lhs_y, lhs_y, Q), addmod(mulmod(lhs_x, mulmod(lhs_x, lhs_x, Q), Q), 3, Q))) + success := and(success, eq(mulmod(rhs_y, rhs_y, Q), addmod(mulmod(rhs_x, mulmod(rhs_x, rhs_x, Q), Q), 3, Q))) - mstore(ACC_LHS_X_MPTR, lhs_x) - mstore(ACC_LHS_Y_MPTR, lhs_y) - mstore(ACC_RHS_X_MPTR, rhs_x) - mstore(ACC_RHS_Y_MPTR, rhs_y) + mstore(add(theta_mptr, 0x100), lhs_x) + mstore(add(theta_mptr, 0x120), lhs_y) + mstore(add(theta_mptr, 0x140), rhs_x) + mstore(add(theta_mptr, 0x160), rhs_y) } - pop(q) } // Revert earlier if anything from calldata is invalid @@ -329,7 +332,7 @@ contract Halo2Verifier { // Compute lagrange evaluations and instance evaluation { let k := mload(add(vk_mptr, 0x100)) - let x := mload(X_MPTR) + let x := mload(add(theta_mptr, 0x80)) let x_n := x for { let idx := 0 } @@ -340,8 +343,8 @@ contract Halo2Verifier { } let omega := mload(add(vk_mptr, 0x140)) - - let mptr := X_N_MPTR + let x_n_mptr := add(theta_mptr, 0x180) + let mptr := x_n_mptr let num_instances := mload(add(vk_mptr,0x60)) let mptr_end := add(mptr, mul(0x20, add(num_instances, {{ num_neg_lagranges }}))) if iszero(num_instances) { @@ -357,9 +360,9 @@ contract Halo2Verifier { } let x_n_minus_1 := addmod(x_n, sub(r, 1), r) mstore(mptr_end, x_n_minus_1) - success := batch_invert(success, X_N_MPTR, add(mptr_end, 0x20), r) + success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20), r) - mptr := X_N_MPTR + mptr := x_n_mptr let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)), r) for { let pow_of_omega := mload(add(vk_mptr, 0x180)) } @@ -370,10 +373,10 @@ contract Halo2Verifier { pow_of_omega := mulmod(pow_of_omega, omega, r) } - let l_blind := mload(add(X_N_MPTR, 0x20)) - let l_i_cptr := add(X_N_MPTR, 0x40) + let l_blind := mload(add(x_n_mptr, 0x20)) + let l_i_cptr := add(x_n_mptr, 0x40) for - { let l_i_cptr_end := add(X_N_MPTR, {{ (num_neg_lagranges * 32)|hex() }}) } + { let l_i_cptr_end := add(x_n_mptr, {{ (num_neg_lagranges * 32)|hex() }}) } lt(l_i_cptr, l_i_cptr_end) { l_i_cptr := add(l_i_cptr, 0x20) } { @@ -396,11 +399,11 @@ contract Halo2Verifier { } let x_n_minus_1_inv := mload(mptr_end) - let l_last := mload(X_N_MPTR) - let l_0 := mload(add(X_N_MPTR, {{ (num_neg_lagranges * 32)|hex() }})) + let l_last := mload(x_n_mptr) + let l_0 := mload(add(x_n_mptr, {{ (num_neg_lagranges * 32)|hex() }})) - mstore(X_N_MPTR, x_n) - mstore(X_N_MINUS_1_INV_MPTR, x_n_minus_1_inv) + mstore(x_n_mptr, x_n) + mstore(add(theta_mptr, 0x1a0), x_n_minus_1_inv) mstore(L_LAST_MPTR, l_last) mstore(L_BLIND_MPTR, l_blind) mstore(L_0_MPTR, l_0) @@ -411,7 +414,7 @@ contract Halo2Verifier { { let quotient_eval_numer let delta := 4131629893567559867359510883348571134090853742863529169391034518566172092834 - let y := mload(Y_MPTR) + let y := mload(add(theta_mptr, 0x60)) {%- for code_block in quotient_eval_numer_computations %} { @@ -424,7 +427,7 @@ contract Halo2Verifier { pop(y) pop(delta) - let quotient_eval := mulmod(quotient_eval_numer, mload(X_N_MINUS_1_INV_MPTR), r) + let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) mstore(QUOTIENT_EVAL_MPTR, quotient_eval) } @@ -432,7 +435,7 @@ contract Halo2Verifier { { mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) mstore(0x20, calldataload(add(mload(add(vk_mptr, 0xa0)), 0x20))) - let x_n := mload(X_N_MPTR) + let x_n := mload(add(theta_mptr, 0x180)) for { let cptr := sub(mload(add(vk_mptr, 0xa0)), 0x40) @@ -462,10 +465,10 @@ contract Halo2Verifier { // Random linear combine with accumulator if mload(add(vk_mptr, 0x01a0)) { - mstore(0x00, mload(ACC_LHS_X_MPTR)) - mstore(0x20, mload(ACC_LHS_Y_MPTR)) - mstore(0x40, mload(ACC_RHS_X_MPTR)) - mstore(0x60, mload(ACC_RHS_Y_MPTR)) + mstore(0x00, mload(add(theta_mptr, 0x100))) + mstore(0x20, mload(add(theta_mptr, 0x120))) + mstore(0x40, mload(add(theta_mptr, 0x140))) + mstore(0x60, mload(add(theta_mptr, 0x160))) mstore(0x80, mload(PAIRING_LHS_X_MPTR)) mstore(0xa0, mload(PAIRING_LHS_Y_MPTR)) mstore(0xc0, mload(PAIRING_RHS_X_MPTR)) From 85a0d2c9c4be4f91e468bc900bafbaf44d754406 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 3 Jul 2024 20:56:49 -0500 Subject: [PATCH 16/65] *cache all theta constants in vk. --- src/codegen.rs | 25 ++++---- src/codegen/evaluator.rs | 46 +++++++++------ src/codegen/pcs.rs | 90 ++++++++++++++--------------- src/codegen/template.rs | 2 - src/codegen/util.rs | 76 ++++++++++++++++++++++-- templates/Halo2VerifierReusable.sol | 82 ++++++++------------------ 6 files changed, 182 insertions(+), 139 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index b25f72e..579f0f5 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -198,8 +198,7 @@ impl<'a> SolidityGenerator<'a> { let g2 = g2_to_u256s(self.params.g2()); let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); - let challenges_length = self.meta.challenge_indices.len(); - let challenges_length = U256::from(challenges_length); + let challenges_offset = self.meta.challenge_indices.len() * 32; vec![ ("vk_digest", vk_digest), @@ -223,7 +222,7 @@ impl<'a> SolidityGenerator<'a> { ("neg_s_g2_x_2", neg_s_g2[1]), ("neg_s_g2_y_1", neg_s_g2[2]), ("neg_s_g2_y_2", neg_s_g2[3]), - ("challenges_length", challenges_length), + ("challenges_offset", U256::from(challenges_offset)), ] }; let fixed_comms: Vec<(U256, U256)> = chain![self.vk.fixed_commitments()] @@ -250,7 +249,13 @@ impl<'a> SolidityGenerator<'a> { let vk_mptr_mock = Ptr::memory( self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), ); - let data = Data::new(&self.meta, &attached_vk, vk_mptr_mock, Ptr::calldata(0x84)); + let data = Data::new( + &self.meta, + &attached_vk, + vk_mptr_mock, + Ptr::calldata(0x84), + true, + ); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); @@ -352,12 +357,12 @@ impl<'a> SolidityGenerator<'a> { let vk = self.generate_vk(false); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); let vk_mptr = Ptr::memory(vk_m); - let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); + let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, false); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), - evaluator.permutation_computations(), + evaluator.permutation_computations(false), evaluator.lookup_computations(None, false).0 ] .enumerate() @@ -421,12 +426,12 @@ impl<'a> SolidityGenerator<'a> { vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); }); - let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr); + let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations = chain![ evaluator.gate_computations(), - evaluator.permutation_computations(), + evaluator.permutation_computations(true), evaluator .lookup_computations(Some(vk_lookup_const_table), true) .0 @@ -454,8 +459,6 @@ impl<'a> SolidityGenerator<'a> { scheme: self.scheme, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, num_evals: self.meta.num_evals, - challenge_mptr: data.challenge_mptr, - theta_mptr: data.theta_mptr, quotient_eval_numer_computations, pcs_computations, } @@ -469,7 +472,7 @@ impl<'a> SolidityGenerator<'a> { let pcs_computation = match self.scheme { Bdfg21 => { let mock_vk_mptr = Ptr::memory(0x100000); - let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr); + let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr, false); let (superset, sets) = rotation_sets(&queries(&self.meta, &mock)); let num_coeffs = sets.iter().map(|set| set.rots().len()).sum::(); 2 * (1 + num_coeffs) + 6 + 2 * superset.len() + 1 + 3 * sets.len() diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index d083734..7f6b522 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -13,6 +13,8 @@ use regex::Regex; use ruint::aliases::U256; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; +use super::util::get_memory_ptr; + #[derive(Debug)] pub(crate) struct Evaluator<'a, F: PrimeField> { cs: &'a ConstraintSystem, @@ -49,13 +51,20 @@ where .collect() } - pub fn permutation_computations(&self) -> Vec<(Vec, String)> { + pub fn permutation_computations(&self, separate: bool) -> Vec<(Vec, String)> { let Self { meta, data, .. } = self; let last_chunk_idx = meta.num_permutation_zs - 1; + let theta_mptr = "theta_mptr"; + let beta = get_memory_ptr(theta_mptr, 1, &separate); + let gamma = get_memory_ptr(theta_mptr, 2, &separate); + let x_mptr = get_memory_ptr(theta_mptr, 4, &separate); + let l_last = get_memory_ptr(theta_mptr, 14, &separate); + let l_blind = get_memory_ptr(theta_mptr, 15, &separate); + let l_0 = get_memory_ptr(theta_mptr, 16, &separate); chain![ data.permutation_z_evals.first().map(|(z, _, _)| { vec![ - format!("let l_0 := mload(L_0_MPTR)"), + format!("let l_0 := mload({l_0})"), format!("let eval := addmod(l_0, sub(r, mulmod(l_0, {z}, r)), r)"), ] }), @@ -63,13 +72,13 @@ where let item = "addmod(mulmod(perm_z_last, perm_z_last, r), sub(r, perm_z_last), r)"; vec![ format!("let perm_z_last := {z}"), - format!("let eval := mulmod(mload(L_LAST_MPTR), {item}, r)"), + format!("let eval := mulmod(mload({l_last}), {item}, r)"), ] }), data.permutation_z_evals.iter().tuple_windows().map( |((_, _, z_i_last), (z_j, _, _))| { let item = format!("addmod({z_j}, sub(r, {z_i_last}), r)"); - vec![format!("let eval := mulmod(mload(L_0_MPTR), {item}, r)")] + vec![format!("let eval := mulmod(mload({l_0}), {item}, r)")] } ), izip!( @@ -81,8 +90,8 @@ where let last_column_idx = columns.len() - 1; chain![ [ - format!("let gamma := mload(GAMMA_MPTR)"), - format!("let beta := mload(BETA_MPTR)"), + format!("let gamma := mload({})", gamma), + format!("let beta := mload({})", beta), format!("let lhs := {}", evals.1), format!("let rhs := {}", evals.0), ], @@ -95,7 +104,7 @@ where )] }), (chunk_idx == 0) - .then(|| "mstore(0x00, mulmod(beta, mload(X_MPTR), r))".to_string()), + .then(|| format!("mstore(0x00, mulmod(beta, mload({}), r))", x_mptr)), columns.iter().enumerate().flat_map(|(idx, column)| { let eval = self.eval(*column.column_type(), column.index(), 0); let item = format!("addmod(addmod({eval}, mload(0x00), r), gamma, r)"); @@ -106,7 +115,7 @@ where ] }), { - let item = format!("addmod(mload(L_LAST_MPTR), mload(L_BLIND_MPTR), r)"); + let item = format!("addmod(mload({l_last}), mload({l_blind}), r)"); let item = format!("sub(r, mulmod(left_sub_right, {item}, r))"); [ format!("let left_sub_right := addmod(lhs, sub(r, rhs), r)"), @@ -188,21 +197,23 @@ where let (phi, phi_next, m) = evals; // if separate then use the theta_mptr on set on the stack // otherwise use the solidity constant - let theta = if separate { "theta_mptr" } else { "THETA_MPTR" }; + let theta_mptr = "theta_mptr"; + let theta = get_memory_ptr(theta_mptr, 0, &separate); // For all the the other pointers offset from the theta_mptr perfrom relavant add operation - let beta = if separate { - "add(theta_mptr, 0x20)" - } else { - "BETA_MPTR" - }; + let beta = get_memory_ptr(theta_mptr, 1, &separate); + let l_last = get_memory_ptr(theta_mptr, 14, &separate); + + let l_blind = get_memory_ptr(theta_mptr, 15, &separate); + + let l_0 = get_memory_ptr(theta_mptr, 16, &separate); // print line the input tables [ vec![ - format!("let l_0 := mload(L_0_MPTR)"), + format!("let l_0 := mload({l_0})"), format!("let eval := mulmod(l_0, {phi}, r)"), ], vec![ - format!("let l_last := mload(L_LAST_MPTR)"), + format!("let l_last := mload({l_last})"), format!("let eval := mulmod(l_last, {phi}, r)"), ], chain![ @@ -299,7 +310,8 @@ where ], ]), { - let l_inactive = "addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), r)"; + let l_inactive = + format!("addmod(mload({l_blind}), mload({l_last}), r)"); let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); [format!( "let eval := mulmod({l_active}, addmod(lhs, sub(r, rhs), r), r)" diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 015b966..627518a 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -1,6 +1,8 @@ #![allow(clippy::useless_format)] -use crate::codegen::util::{for_loop, ConstraintSystemMeta, Data, EcPoint, Location, Ptr, Word}; +use crate::codegen::util::{ + for_loop, get_memory_ptr, ConstraintSystemMeta, Data, EcPoint, Location, Ptr, Word, +}; use itertools::{chain, izip, Itertools}; use std::collections::{BTreeMap, BTreeSet}; @@ -251,36 +253,23 @@ pub(crate) fn bdfg21_computations( // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. // Otherwise we load in omega and omega_inv from the solidity constants. - let omega = if separate { - "add(vk_mptr, 0x140)" - } else { - "OMEGA_MPTR" - }; - let omega_inv = if separate { - "add(vk_mptr, 0x160)" - } else { - "OMEGA_INV_MPTR" - }; - - let g1_x = if separate { - "add(vk_mptr, 0x0220)" - } else { - "G1_X_MPTR" - }; - let g1_y = if separate { - "add(vk_mptr, 0x0240)" - } else { - "G1_Y_MPTR" - }; - - let x_mptr = if separate { - "add(theta_mptr, 0x80)" - } else { - "X_MPTR" - }; + let vk_mptr = "vk_mptr"; + let theta_mptr = "theta_mptr"; + + let omega = get_memory_ptr(vk_mptr, 10, &separate); + + let omega_inv = get_memory_ptr(vk_mptr, 11, &separate); + + let g1_x = get_memory_ptr(vk_mptr, 17, &separate); + + let g1_y = get_memory_ptr(vk_mptr, 18, &separate); + + let x = get_memory_ptr(theta_mptr, 4, &separate); + + let zeta = get_memory_ptr(theta_mptr, 5, &separate); let point_computations = chain![ [ - format!("let x := mload({})", x_mptr).as_str(), + format!("let x := mload({})", x).as_str(), format!("let omega := mload({})", omega).as_str(), format!("let omega_inv := mload({})", omega_inv).as_str(), "let x_pow_of_omega := mulmod(x, omega, r)" @@ -311,9 +300,9 @@ pub(crate) fn bdfg21_computations( }) ] .collect_vec(); - + let mu = get_memory_ptr(theta_mptr, 7, &separate); let vanishing_computations = chain![ - ["let mu := mload(MU_MPTR)".to_string()], + [format!("let mu := mload({mu})").to_string()], { let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); let mptr_end = mptr + mu_minus_points.len(); @@ -425,7 +414,11 @@ pub(crate) fn bdfg21_computations( let is_single_rot_set = set.rots().len() == 1; chain![ is_single_rot_set.then(|| format!("let coeff := {}", coeffs[0])), - ["let zeta := mload(ZETA_MPTR)", "let r_eval := 0"].map(str::to_string), + [ + format!("let zeta := mload({})", zeta).as_str(), + "let r_eval := 0" + ] + .map(str::to_string), if is_single_rot_set { let eval_groups = set.evals().iter().rev().fold( Vec::>::new(), @@ -509,6 +502,13 @@ pub(crate) fn bdfg21_computations( .collect_vec() }); + let nu = get_memory_ptr(theta_mptr, 6, &separate); + let mu = get_memory_ptr(theta_mptr, 7, &separate); + let r_eval = get_memory_ptr(theta_mptr, 21, &separate); + let pairing_lhs_x = get_memory_ptr(theta_mptr, 22, &separate); + let pairing_lhs_y = get_memory_ptr(theta_mptr, 23, &separate); + let pairing_rhs_x = get_memory_ptr(theta_mptr, 24, &separate); + let pairing_rhs_y = get_memory_ptr(theta_mptr, 25, &separate); let r_eval_computations = chain![ for_loop( [ @@ -541,17 +541,17 @@ pub(crate) fn bdfg21_computations( ] .map(str::to_string), [ - "r_eval := mulmod(r_eval, mload(NU_MPTR), r)", + format!("r_eval := mulmod(r_eval, mload({nu}), r)").as_str(), "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), r), r)" ] .map(str::to_string), ), - ["mstore(R_EVAL_MPTR, r_eval)".to_string()], + [format!("mstore({r_eval}, r_eval)")], ] .collect_vec(); let pairing_input_computations = chain![ - ["let nu := mload(NU_MPTR)".to_string()], + [format!("let nu := mload({nu})").to_string()], izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, set_coeff)| { let is_first_set = set_idx == 0; let is_last_set = set_idx == sets.len() - 1; @@ -580,7 +580,7 @@ pub(crate) fn bdfg21_computations( } }, ); - + let zeta_mptr = ζ chain![ set.comms() .last() @@ -599,7 +599,7 @@ pub(crate) fn bdfg21_computations( .flat_map(|comm| { let (x, y) = (comm.x(), comm.y()); [ - format!("success := {ec_mul}(success, mload(ZETA_MPTR))"), + format!("success := {ec_mul}(success, mload({zeta_mptr}))"), format!("success := {ec_add}(success, {x}, {y})"), ] }) @@ -617,7 +617,7 @@ pub(crate) fn bdfg21_computations( "lt(mptr_end, mptr)", ["mptr := sub(mptr, 0x40)".to_string()], [ - format!("success := {ec_mul}(success, mload(ZETA_MPTR))"), + format!("success := {ec_mul}(success, mload({zeta_mptr}))"), format!("success := {ec_add}(success, {x}, {y})"), ], ) @@ -631,7 +631,7 @@ pub(crate) fn bdfg21_computations( format!("success := ec_mul_tmp(success, {scalar})"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), ], - (!is_last_set).then(|| format!("nu := mulmod(nu, mload(NU_MPTR), r)")) + (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), r)")) ] }) .into_iter() @@ -642,7 +642,7 @@ pub(crate) fn bdfg21_computations( [ format!("mstore(0x80, mload({}))", g1_x), format!("mstore(0xa0, mload({}))", g1_y), - format!("success := ec_mul_tmp(success, sub(r, mload(R_EVAL_MPTR)))"), + format!("success := ec_mul_tmp(success, sub(r, mload({r_eval})))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w.x()), format!("mstore(0xa0, {})", w.y()), @@ -650,12 +650,12 @@ pub(crate) fn bdfg21_computations( format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w_prime.x()), format!("mstore(0xa0, {})", w_prime.y()), - format!("success := ec_mul_tmp(success, mload(MU_MPTR))"), + format!("success := ec_mul_tmp(success, mload({mu}))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - format!("mstore(PAIRING_LHS_X_MPTR, mload(0x00))"), - format!("mstore(PAIRING_LHS_Y_MPTR, mload(0x20))"), - format!("mstore(PAIRING_RHS_X_MPTR, {})", w_prime.x()), - format!("mstore(PAIRING_RHS_Y_MPTR, {})", w_prime.y()), + format!("mstore({pairing_lhs_x}, mload(0x00))"), + format!("mstore({pairing_lhs_y}, mload(0x20))"), + format!("mstore({pairing_rhs_x}, {})", w_prime.x()), + format!("mstore({pairing_rhs_y}, {})", w_prime.y()), ], ] .collect_vec(); diff --git a/src/codegen/template.rs b/src/codegen/template.rs index b0f3130..2ccd260 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -50,8 +50,6 @@ pub(crate) struct Halo2Verifier { #[template(path = "Halo2VerifierReusable.sol")] pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, - pub(crate) challenge_mptr: Ptr, - pub(crate) theta_mptr: Ptr, pub(crate) num_neg_lagranges: usize, pub(crate) num_evals: usize, pub(crate) quotient_eval_numer_computations: Vec>, diff --git a/src/codegen/util.rs b/src/codegen/util.rs index f6cd46c..1aa021c 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -394,6 +394,7 @@ impl Data { vk: &Halo2VerifyingKey, vk_mptr: Ptr, proof_cptr: Ptr, + separate: bool, ) -> Self { let fixed_comm_mptr = vk_mptr + vk.constants.len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); @@ -418,6 +419,26 @@ impl Data { let permutation_z_eval_cptr = permutation_eval_cptr + meta.num_permutations(); let lookup_eval_cptr = permutation_z_eval_cptr + 3 * meta.num_permutation_zs - 1; let w_cptr = lookup_eval_cptr + 3 * meta.num_lookups(); + let l_0 = if separate { + "add(theta_mptr, 0x220)" + } else { + "INSTANCE_EVAL_MPTR" + }; + let quotient_eval = if separate { + "add(theta_mptr, 0x240)" + } else { + "QUOTIENT_EVAL_MPTR" + }; + let quotient_x = if separate { + "add(theta_mptr, 0x260)" + } else { + "QUOTIENT_X_MPTR" + }; + let quotient_y = if separate { + "add(theta_mptr, 0x280)" + } else { + "QUOTIENT_Y_MPTR" + }; let fixed_comms = EcPoint::range(fixed_comm_mptr) .take(meta.num_fixeds) @@ -443,10 +464,7 @@ impl Data { .take(meta.num_lookup_phis) .collect(); let random_comm = random_comm_start.into(); - let computed_quotient_comm = EcPoint::new( - Ptr::memory("QUOTIENT_X_MPTR"), - Ptr::memory("QUOTIENT_Y_MPTR"), - ); + let computed_quotient_comm = EcPoint::new(Ptr::memory(quotient_x), Ptr::memory(quotient_y)); let challenges = meta .challenge_indices @@ -454,7 +472,7 @@ impl Data { .map(|idx| challenge_mptr + *idx) .map_into() .collect_vec(); - let instance_eval = Ptr::memory("INSTANCE_EVAL_MPTR").into(); + let instance_eval = Ptr::memory(l_0).into(); let advice_evals = izip!( meta.advice_queries.iter().cloned(), Word::range(advice_eval_cptr) @@ -479,7 +497,7 @@ impl Data { .take(3 * meta.num_lookup_phis) .tuples() .collect_vec(); - let computed_quotient_eval = Ptr::memory("QUOTIENT_EVAL_MPTR").into(); + let computed_quotient_eval = Ptr::memory(quotient_eval).into(); Self { challenge_mptr, @@ -517,6 +535,7 @@ impl Data { vk: &Halo2VerifyingKey, vk_mptr: Ptr, proof_cptr: Ptr, + separate: bool, ) -> Self { let fixed_comm_mptr = vk_mptr + vk.constants.len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); @@ -925,3 +944,48 @@ where { U256::from(value).to_be_bytes() } + +pub(crate) fn get_memory_ptr(base_ptr: &str, offset: usize, separate: &bool) -> String { + if *separate { + if offset != 0 { + let hex_offset = format!("{:X}", offset * 32); + format!("add({}, 0x{})", base_ptr, hex_offset) + } else { + base_ptr.to_string() + } + } else { + match (offset, base_ptr) { + (10, "vk_mptr") => "OMEGA_MPTR".to_string(), + (11, "vk_mptr") => "OMEGA_INV_MPTR".to_string(), + (17, "vk_mptr") => "G1_X_MPTR".to_string(), + (18, "vk_mptr") => "G1_Y_MPTR".to_string(), + (0, "theta_mptr") => "THETA_MPTR".to_string(), + (1, "theta_mptr") => "BETA_MPTR".to_string(), + (2, "theta_mptr") => "GAMMA_MPTR".to_string(), + (3, "theta_mptr") => "Y_MPTR".to_string(), + (4, "theta_mptr") => "X_MPTR".to_string(), + (5, "theta_mptr") => "ZETA_MPTR".to_string(), + (6, "theta_mptr") => "NU_MPTR".to_string(), + (7, "theta_mptr") => "MU_MPTR".to_string(), + (8, "theta_mptr") => "ACC_LHS_X_MPTR".to_string(), + (9, "theta_mptr") => "ACC_LHS_Y_MPTR".to_string(), + (10, "theta_mptr") => "ACC_RHS_X_MPTR".to_string(), + (11, "theta_mptr") => "ACC_RHS_Y_MPTR".to_string(), + (12, "theta_mptr") => "X_N_MPTR".to_string(), + (13, "theta_mptr") => "X_N_MINUS_1_INV_MPTR".to_string(), + (14, "theta_mptr") => "L_LAST_MPTR".to_string(), + (15, "theta_mptr") => "L_BLIND_MPTR".to_string(), + (16, "theta_mptr") => "L_0_MPTR".to_string(), + (17, "theta_mptr") => "INSTANCE_EVAL_MPTR".to_string(), + (18, "theta_mptr") => "QUOTIENT_EVAL_MPTR".to_string(), + (19, "theta_mptr") => "QUOTIENT_X_MPTR".to_string(), + (20, "theta_mptr") => "QUOTIENT_Y_MPTR".to_string(), + (21, "theta_mptr") => "R_EVAL_MPTR".to_string(), + (22, "theta_mptr") => "PAIRING_LHS_X_MPTR".to_string(), + (23, "theta_mptr") => "PAIRING_LHS_Y_MPTR".to_string(), + (24, "theta_mptr") => "PAIRING_RHS_X_MPTR".to_string(), + (25, "theta_mptr") => "PAIRING_RHS_Y_MPTR".to_string(), + _ => panic!("Unknown offset: {}", offset), + } + } +} diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 1b9410a..1209cf8 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -7,40 +7,6 @@ contract Halo2Verifier { uint256 internal constant PROOF_CPTR = 0x84; uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - // TODO finish offseting the memory pointers in the permutation columns method - uint256 internal constant THETA_MPTR = {{ theta_mptr }}; - uint256 internal constant BETA_MPTR = {{ theta_mptr + 1 }}; - uint256 internal constant GAMMA_MPTR = {{ theta_mptr + 2 }}; - uint256 internal constant Y_MPTR = {{ theta_mptr + 3 }}; - uint256 internal constant X_MPTR = {{ theta_mptr + 4 }}; - {%- match scheme %} - {%- when Bdfg21 %} - uint256 internal constant ZETA_MPTR = {{ theta_mptr + 5 }}; - uint256 internal constant NU_MPTR = {{ theta_mptr + 6 }}; - uint256 internal constant MU_MPTR = {{ theta_mptr + 7 }}; - {%- when Gwc19 %} - // TODO - {%- endmatch %} - - uint256 internal constant ACC_LHS_X_MPTR = {{ theta_mptr + 8 }}; - uint256 internal constant ACC_LHS_Y_MPTR = {{ theta_mptr + 9 }}; - uint256 internal constant ACC_RHS_X_MPTR = {{ theta_mptr + 10 }}; - uint256 internal constant ACC_RHS_Y_MPTR = {{ theta_mptr + 11 }}; - uint256 internal constant X_N_MPTR = {{ theta_mptr + 12 }}; - uint256 internal constant X_N_MINUS_1_INV_MPTR = {{ theta_mptr + 13 }}; - uint256 internal constant L_LAST_MPTR = {{ theta_mptr + 14 }}; // X NEEDED from here on - uint256 internal constant L_BLIND_MPTR = {{ theta_mptr + 15 }}; - uint256 internal constant L_0_MPTR = {{ theta_mptr + 16 }}; - uint256 internal constant INSTANCE_EVAL_MPTR = {{ theta_mptr + 17 }}; - uint256 internal constant QUOTIENT_EVAL_MPTR = {{ theta_mptr + 18 }}; - uint256 internal constant QUOTIENT_X_MPTR = {{ theta_mptr + 19 }}; - uint256 internal constant QUOTIENT_Y_MPTR = {{ theta_mptr + 20 }}; - uint256 internal constant R_EVAL_MPTR = {{ theta_mptr + 21 }}; - uint256 internal constant PAIRING_LHS_X_MPTR = {{ theta_mptr + 22 }}; - uint256 internal constant PAIRING_LHS_Y_MPTR = {{ theta_mptr + 23 }}; - uint256 internal constant PAIRING_RHS_X_MPTR = {{ theta_mptr + 24 }}; - uint256 internal constant PAIRING_RHS_Y_MPTR = {{ theta_mptr + 25 }}; - function verifyProof( address vk, bytes calldata proof, @@ -404,10 +370,10 @@ contract Halo2Verifier { mstore(x_n_mptr, x_n) mstore(add(theta_mptr, 0x1a0), x_n_minus_1_inv) - mstore(L_LAST_MPTR, l_last) - mstore(L_BLIND_MPTR, l_blind) - mstore(L_0_MPTR, l_0) - mstore(INSTANCE_EVAL_MPTR, instance_eval) + mstore(add(theta_mptr, 0x1c0), l_last) + mstore(add(theta_mptr, 0x1e0), l_blind) + mstore(add(theta_mptr, 0x200), l_0) + mstore(add(theta_mptr, 0x220), instance_eval) } // Compute quotient evavluation @@ -428,7 +394,7 @@ contract Halo2Verifier { pop(delta) let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) - mstore(QUOTIENT_EVAL_MPTR, quotient_eval) + mstore(add(theta_mptr, 0x240), quotient_eval) } // Compute quotient commitment @@ -448,8 +414,8 @@ contract Halo2Verifier { success := ec_add_acc(success, calldataload(cptr), calldataload(add(cptr, 0x20))) cptr := sub(cptr, 0x40) } - mstore(QUOTIENT_X_MPTR, mload(0x00)) - mstore(QUOTIENT_Y_MPTR, mload(0x20)) + mstore(add(theta_mptr, 0x260), mload(0x00)) + mstore(add(theta_mptr, 0x280), mload(0x20)) } // Compute pairing lhs and rhs @@ -469,35 +435,35 @@ contract Halo2Verifier { mstore(0x20, mload(add(theta_mptr, 0x120))) mstore(0x40, mload(add(theta_mptr, 0x140))) mstore(0x60, mload(add(theta_mptr, 0x160))) - mstore(0x80, mload(PAIRING_LHS_X_MPTR)) - mstore(0xa0, mload(PAIRING_LHS_Y_MPTR)) - mstore(0xc0, mload(PAIRING_RHS_X_MPTR)) - mstore(0xe0, mload(PAIRING_RHS_Y_MPTR)) + mstore(0x80, mload(add(theta_mptr, 0x2c0))) + mstore(0xa0, mload(add(theta_mptr, 0x2e0))) + mstore(0xc0, mload(add(theta_mptr, 0x300))) + mstore(0xe0, mload(add(theta_mptr, 0x320))) let challenge := mod(keccak256(0x00, 0x100), r) // [pairing_lhs] += challenge * [acc_lhs] success := ec_mul_acc(success, challenge) - success := ec_add_acc(success, mload(PAIRING_LHS_X_MPTR), mload(PAIRING_LHS_Y_MPTR)) - mstore(PAIRING_LHS_X_MPTR, mload(0x00)) - mstore(PAIRING_LHS_Y_MPTR, mload(0x20)) + success := ec_add_acc(success, mload(add(theta_mptr, 0x2c0)), mload(add(theta_mptr, 0x2e0))) + mstore(add(theta_mptr, 0x2c0), mload(0x00)) + mstore(add(theta_mptr, 0x2e0), mload(0x20)) // [pairing_rhs] += challenge * [acc_rhs] - mstore(0x00, mload(ACC_RHS_X_MPTR)) - mstore(0x20, mload(ACC_RHS_Y_MPTR)) + mstore(0x00, mload(add(theta_mptr, 0x140))) + mstore(0x20, mload(add(theta_mptr, 0x160))) success := ec_mul_acc(success, challenge) - success := ec_add_acc(success, mload(PAIRING_RHS_X_MPTR), mload(PAIRING_RHS_Y_MPTR)) - mstore(PAIRING_RHS_X_MPTR, mload(0x00)) - mstore(PAIRING_RHS_Y_MPTR, mload(0x20)) + success := ec_add_acc(success, mload(add(theta_mptr, 0x300)), mload(add(theta_mptr, 0x320))) + mstore(add(theta_mptr, 0x300), mload(0x00)) + mstore(add(theta_mptr, 0x320), mload(0x20)) } - // Perform pairing + // // Perform pairing success := ec_pairing( success, vk_mptr, - mload(PAIRING_LHS_X_MPTR), - mload(PAIRING_LHS_Y_MPTR), - mload(PAIRING_RHS_X_MPTR), - mload(PAIRING_RHS_Y_MPTR) + mload(add(theta_mptr, 0x2c0)), + mload(add(theta_mptr, 0x2e0)), + mload(add(theta_mptr, 0x300)), + mload(add(theta_mptr, 0x320)) ) From 57a969b0815761288995120684fbab40e9e83896 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 6 Jul 2024 23:01:49 -0500 Subject: [PATCH 17/65] *set delta as a constant *allocate gate_computations_len memory space in VK --- src/codegen.rs | 174 ++++++++++++++++++---------- src/codegen/evaluator.rs | 7 +- src/codegen/template.rs | 2 + src/codegen/util.rs | 5 +- src/test.rs | 5 + templates/Halo2Verifier.sol | 3 +- templates/Halo2VerifierReusable.sol | 79 ++++++++++++- templates/Halo2VerifyingKey.sol | 10 +- 8 files changed, 210 insertions(+), 75 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 579f0f5..0d65824 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -198,32 +198,66 @@ impl<'a> SolidityGenerator<'a> { let g2 = g2_to_u256s(self.params.g2()); let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); - let challenges_offset = self.meta.challenge_indices.len() * 32; - - vec![ - ("vk_digest", vk_digest), - ("num_instances", num_instances), - ("k", k), - ("n_inv", n_inv), - ("omega", omega), - ("omega_inv", omega_inv), - ("omega_inv_to_l", omega_inv_to_l), - ("has_accumulator", has_accumulator), - ("acc_offset", acc_offset), - ("num_acc_limbs", num_acc_limbs), - ("num_acc_limb_bits", num_acc_limb_bits), - ("g1_x", g1[0]), - ("g1_y", g1[1]), - ("g2_x_1", g2[0]), - ("g2_x_2", g2[1]), - ("g2_y_1", g2[2]), - ("g2_y_2", g2[3]), - ("neg_s_g2_x_1", neg_s_g2[0]), - ("neg_s_g2_x_2", neg_s_g2[1]), - ("neg_s_g2_y_1", neg_s_g2[2]), - ("neg_s_g2_y_2", neg_s_g2[3]), - ("challenges_offset", U256::from(challenges_offset)), - ] + if separate { + vec![ + ("vk_digest", vk_digest), + ("vk_mptr", U256::from(0)), // dummy vk_mptr + ("vk_len", U256::from(0)), // dummy vk_len + ("num_instances", num_instances), + ("num_advices_user_challenges_offset", U256::from(0)), // dummy num_advices_user_challenges_offset + ("last_quotient_x_cptr", U256::from(0)), // dummy last_quotient_x_cptr + ("first_quotient_x_cptr", U256::from(0)), // dummy first_quotient_x_cptr + ("instance_cptr", U256::from(0)), // dummy instance_cptr + ("k", k), + ("n_inv", n_inv), + ("omega", omega), + ("omega_inv", omega_inv), + ("omega_inv_to_l", omega_inv_to_l), + ("has_accumulator", has_accumulator), + ("acc_offset", acc_offset), + ("num_acc_limbs", num_acc_limbs), + ("num_acc_limb_bits", num_acc_limb_bits), + ("g1_x", g1[0]), + ("g1_y", g1[1]), + ("g2_x_1", g2[0]), + ("g2_x_2", g2[1]), + ("g2_y_1", g2[2]), + ("g2_y_2", g2[3]), + ("neg_s_g2_x_1", neg_s_g2[0]), + ("neg_s_g2_x_2", neg_s_g2[1]), + ("neg_s_g2_y_1", neg_s_g2[2]), + ("neg_s_g2_y_2", neg_s_g2[3]), + ( + "challenges_offset", + U256::from(self.meta.challenge_indices.len() * 32), + ), + ("gate_computations_len_offset", U256::from(0)), // dummy gate_computations_len_offset + ] + } else { + vec![ + ("vk_digest", vk_digest), + ("num_instances", num_instances), + ("k", k), + ("n_inv", n_inv), + ("omega", omega), + ("omega_inv", omega_inv), + ("omega_inv_to_l", omega_inv_to_l), + ("has_accumulator", has_accumulator), + ("acc_offset", acc_offset), + ("num_acc_limbs", num_acc_limbs), + ("num_acc_limb_bits", num_acc_limb_bits), + ("g1_x", g1[0]), + ("g1_y", g1[1]), + ("g2_x_1", g2[0]), + ("g2_x_2", g2[1]), + ("g2_y_1", g2[2]), + ("g2_y_2", g2[3]), + ("neg_s_g2_x_1", neg_s_g2[0]), + ("neg_s_g2_x_2", neg_s_g2[1]), + ("neg_s_g2_y_1", neg_s_g2[2]), + ("neg_s_g2_y_2", neg_s_g2[3]), + ] + } }; let fixed_comms: Vec<(U256, U256)> = chain![self.vk.fixed_commitments()] .flat_map(g1_to_u256s) @@ -240,6 +274,7 @@ impl<'a> SolidityGenerator<'a> { permutation_comms: permutation_comms.clone(), const_lookup_input_expressions: vec![], num_advices_user_challenges: vec![], + gate_computations_lens: vec![], }; if !separate { @@ -267,51 +302,28 @@ impl<'a> SolidityGenerator<'a> { let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); - // insert it at position 3 of the constants. - constants.insert(2, ("instance_cptr", instance_cptr)); + // set instance_cptr it at position 7 of the constants. + constants[7] = ("instance_cptr", instance_cptr); let first_quotient_x_cptr = data.quotient_comm_cptr; - constants.insert( - 2, - ( - "first_quotient_x_cptr", - U256::from(first_quotient_x_cptr.value().as_usize()), - ), + // set first_quotient_x_cptr at position 6 of the constants. + constants[6] = ( + "first_quotient_x_cptr", + U256::from(first_quotient_x_cptr.value().as_usize()), ); let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); - constants.insert( - 2, - ( - "last_quotient_x_cptr", - U256::from(last_quotient_x_cptr.value().as_usize()), - ), + // set last_quotient_x_cptr at position 5 of the constants. + constants[5] = ( + "last_quotient_x_cptr", + U256::from(last_quotient_x_cptr.value().as_usize()), ); - // insert mock vk_mptr_mock at position 0 - constants.insert(1, ("vk_mptr", U256::from(vk_mptr_mock.value().as_usize()))); - - // insert mock vk_len at position 1 - - let vk_len_mock = attached_vk.len(); - - constants.insert(2, ("vk_len", U256::from(vk_len_mock))); - - let num_advices_user_challenges_offset = (constants.len() * 0x20) - + (fixed_comms.len() + permutation_comms.len()) * 0x40 - + (const_lookup_input_expressions.len() * 0x20) - + 0x20; - - // insert it at position 3 of the constants. - constants.insert( - 4, - ( - "num_advices_user_challenges_offset", - U256::from(num_advices_user_challenges_offset), - ), - ); + let gate_computations_lens: Vec = chain![evaluator.gate_computations()] + .map(|(lines, _)| U256::from(lines.len())) + .collect(); let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); @@ -330,12 +342,34 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); + let gate_computations_len_offset = (constants.len() * 0x20) + + (fixed_comms.len() + permutation_comms.len()) * 0x40 + + (const_lookup_input_expressions.len() * 0x20) + + ((num_advices_user_challenges.len() * 0x40) + 0x20); + + // set the gate_computations_len_offset at position 28. + constants[28] = ( + "gate_computations_len_offset", + U256::from(gate_computations_len_offset), + ); + + let num_advices_user_challenges_offset = (constants.len() * 0x20) + + (fixed_comms.len() + permutation_comms.len()) * 0x40 + + (const_lookup_input_expressions.len() * 0x20); + + // set the num_advices_user_challenges_offset at position 4 + constants[4] = ( + "num_advices_user_challenges_offset", + U256::from(num_advices_user_challenges_offset), + ); + let mut vk = Halo2VerifyingKey { constants, fixed_comms, permutation_comms, const_lookup_input_expressions, num_advices_user_challenges, + gate_computations_lens, }; // new generate the real vk_mptr let vk_mptr = Ptr::memory( @@ -360,7 +394,7 @@ impl<'a> SolidityGenerator<'a> { let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, false); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - let quotient_eval_numer_computations = chain![ + let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), evaluator.permutation_computations(false), evaluator.lookup_computations(None, false).0 @@ -429,7 +463,7 @@ impl<'a> SolidityGenerator<'a> { let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - let quotient_eval_numer_computations = chain![ + let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), evaluator.permutation_computations(true), evaluator @@ -449,6 +483,17 @@ impl<'a> SolidityGenerator<'a> { lines }) .collect(); + // iterate through the quotient_eval_numer_computations and determine longest Vec within the Vec>. + // TODO: Use this to estimate static working memory size + // let quotient_eval_numer_computations_longest = quotient_eval_numer_computations + // .iter() + // .max_by_key(|x| x.len()) + // .unwrap() + // .clone(); + // println!( + // "longest computation: {:?}", + // quotient_eval_numer_computations_longest.len() + // ); let pcs_computations = match self.scheme { Bdfg21 => bdfg21_computations(&self.meta, &data, true), @@ -469,6 +514,7 @@ impl<'a> SolidityGenerator<'a> { vk: &Halo2VerifyingKey, proof_cptr: Ptr, ) -> usize { + // TODO add a check for the amount of memory required for the compute quotient evavluation let pcs_computation = match self.scheme { Bdfg21 => { let mock_vk_mptr = Ptr::memory(0x100000); diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 7f6b522..bd3681d 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -111,7 +111,7 @@ where chain![ [format!("rhs := mulmod(rhs, {item}, r)")], (!(chunk_idx == last_chunk_idx && idx == last_column_idx)) - .then(|| "mstore(0x00, mulmod(mload(0x00), delta, r))".to_string()), + .then(|| "mstore(0x00, mulmod(mload(0x00), DELTA, r))".to_string()), ] }), { @@ -223,6 +223,7 @@ where "let table" ] .map(str::to_string), + // TODO: break this into it's own function on the solidity side of things code_block::<1, false>(chain![ table_lines, [format!("table := {table_0}")], @@ -231,6 +232,8 @@ where )), [format!("table := addmod(table, beta, r)")], ]), + // TODO: break this into it's own function on the solidity side of things, + // calling it within a for loop. izip!(0.., inputs.into_iter()).flat_map(|(idx, (input_lines, inputs))| { let (input_0, rest_inputs) = inputs.split_first().unwrap(); let ident = format!("input_{idx}"); @@ -277,6 +280,7 @@ where ] }), [format!("let lhs"), format!("let rhs")], + // TODO: break this into it's own function on the solidity side of things (0..num_inputs).flat_map(|i| { assert_ne!(num_inputs, 0); if num_inputs == 1 { @@ -297,6 +301,7 @@ where ]) } }), + // TODO: break this into it's own function on the solidity side of things code_block::<1, false>(chain![ [format!("let tmp := input_0")], (1..num_inputs) diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 2ccd260..0469480 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -14,6 +14,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_lookup_input_expressions: Vec, + pub(crate) gate_computations_lens: Vec, } impl Halo2VerifyingKey { @@ -22,6 +23,7 @@ impl Halo2VerifyingKey { + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + (self.const_lookup_input_expressions.len() * 0x20) + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) + + (self.gate_computations_lens.len() * 0x20 + 0x20) } } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 1aa021c..3a7a2fa 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -401,7 +401,8 @@ impl Data { let challenge_mptr = permutation_comm_mptr + (2 * vk.permutation_comms.len()) + vk.const_lookup_input_expressions.len() - + (2 * vk.num_advices_user_challenges.len() + 1); + + (2 * vk.num_advices_user_challenges.len() + 1) + + (vk.gate_computations_lens.len() + 1); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; @@ -535,7 +536,7 @@ impl Data { vk: &Halo2VerifyingKey, vk_mptr: Ptr, proof_cptr: Ptr, - separate: bool, + _separate: bool, ) -> Self { let fixed_comm_mptr = vk_mptr + vk.constants.len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); diff --git a/src/test.rs b/src/test.rs index 33e5d50..e2518e0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -53,6 +53,7 @@ fn run_render>() { let generator = SolidityGenerator::new(¶ms, &vk, Bdfg21, instances.len()) .set_acc_encoding(acc_encoding); let verifier_solidity = generator.render().unwrap(); + // println!("Verifier solidity conjoined: {verifier_solidity}"); let verifier_creation_code = compile_solidity(verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); @@ -96,6 +97,10 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); + // print verifier_solidity + // println!("Verifier solidity: {verifier_solidity}"); + // print vk_solidity + // println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); let vk_address = evm.create(vk_creation_code); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 22136cb..47f8851 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; contract Halo2Verifier { + uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; uint256 internal constant PROOF_LEN_CPTR = {{ proof_len_cptr }}; uint256 internal constant PROOF_CPTR = {{ proof_cptr }}; uint256 internal constant NUM_INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) }}; @@ -440,7 +441,6 @@ contract Halo2Verifier { // Compute quotient evavluation { let quotient_eval_numer - let delta := 4131629893567559867359510883348571134090853742863529169391034518566172092834 let y := mload(Y_MPTR) {%- for code_block in quotient_eval_numer_computations %} @@ -452,7 +452,6 @@ contract Halo2Verifier { {%- endfor %} pop(y) - pop(delta) let quotient_eval := mulmod(quotient_eval_numer, mload(X_N_MINUS_1_INV_MPTR), r) mstore(QUOTIENT_EVAL_MPTR, quotient_eval) diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 1209cf8..2fe15fa 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -4,8 +4,11 @@ pragma solidity ^0.8.0; contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; - uint256 internal constant PROOF_CPTR = 0x84; - uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant PROOF_CPTR = 0x84; + uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; + + function verifyProof( address vk, @@ -379,7 +382,6 @@ contract Halo2Verifier { // Compute quotient evavluation { let quotient_eval_numer - let delta := 4131629893567559867359510883348571134090853742863529169391034518566172092834 let y := mload(add(theta_mptr, 0x60)) {%- for code_block in quotient_eval_numer_computations %} @@ -391,12 +393,81 @@ contract Halo2Verifier { {%- endfor %} pop(y) - pop(delta) let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) mstore(add(theta_mptr, 0x240), quotient_eval) } + // // Compute quotient evavluation + // // TODO: + // // [X] Gate computations + // // [ ] Permutation computations + // // [ ] Lookup computations + // { + // let quotient_eval_numer + // let y := mload(add(theta_mptr, 0x60)) + // let code_blocks_len := mload(add(vk_mptr, 0x380)) // Remember this length represented in bytes + // let code_blocks_len_offset := mload(add(vk_mptr, CODE_BLOCKS_LENS_OFFSET)) // TODO fill in the correct offset + // let expressions_ptr := add(vk_mptr, EXPRESSIONS_OFFSET) // TODO fill in the correct offset + // let expression := 0x0 // Initialize this to 0. Will set it later in the loop + // let experssion_words_counter := 0 + // let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks + // let constants_ptr := add(vk_mptr, CONSTANTS_OFFSET) // TODO fill in the correct offset + // // Load in the total number of code blocks from the vk constants, right after the number challenges + // for { let code_block := 0 } lt(code_block, code_blocks_len) { code_block := add(code_block, 0x20) } { + // // Shift the code_len by the free_static_memory_ptr + // let code_len := add(mload(add(code_blocks_len_offset, code_block)), free_static_memory_ptr) + // // loop through code len + // for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { + // /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word + // expression := mload(add(expressions_ptr, experssion_words_counter)) + // experssion_words_counter := add(experssion_words_counter, 0x20) + // // Load in the least significant byte located of the `expression` word to get the operation type + // let byte := and(expression, 0xFF) + + // // Determine which operation to peform and then store the result in the next available memory slot. + + // // 0x00 => Advice/Fixed expression + // if (eq(byte, 0x00)) { + // // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + // mstore(i,calldataload(and(shr(8, expressions), 0xFFFF))) + // } + // // 0x01 => Negated expression + // else if (eq(byte, 0x01)) { + // // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + // mstore(i,sub(r, mload(and(shr(8, expressions), 0xFFFF)))) + // } + // // 0x02 => Sum expression + // else if (eq(byte, 0x02)) { + // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + // mstore(i,addmod(mload(and(shr(24, expressions), 0xFFFF)),mload(and(shr(8, expressions), 0xFFFF)),r)) + // } + // // 0x03 => Product/scalar expression + // else if (eq(byte, 0x03)) { + // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + // mstore(i,mulmod(mload(and(shr(24, expressions), 0xFFFF)),mload(and(shr(8, expressions), 0xFFFF)),r)) + // } + // } + // // at the end of each code block we update `quotient_eval_numer` + // if (eq(code_block, 0x00)) { + // // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block + // quotient_eval_numer := mload(code_len) + // } else { + // // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r + // quotient_eval_numer := addmod(quotient_eval_numer, mload(code_len), r) + // } + // } + + // pop(y) + + // let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) + // mstore(add(theta_mptr, 0x240), quotient_eval) + // // Check that the quotient evaluation is correct + // success := and(success, eq(quotient_eval, mload(add(theta_mptr, 0x240))) + // } + // Compute quotient commitment { mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index eef7e4b..8a777b2 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -19,7 +19,7 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} {%- for const in const_lookup_input_expressions %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} + {%- let offset =constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_lookup_input_expressions[{{ loop.index0 }}] {%- endfor %} {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len() %} @@ -29,7 +29,13 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // num_advices[{{ loop.index0 }}].x mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 1))|hex() }}) + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 1 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // gate_computations_lens length + {%- for gate_computations_len in gate_computations_lens %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 2 %} + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ gate_computations_len|hex_padded(64) }}) // gate_computations_len[{{ loop.index0 }}] + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 2 + gate_computations_lens.len()))|hex() }}) } } } From 9331a92ce81aa7bdf45340ef1b55029824a40792 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 9 Jul 2024 17:46:15 -0500 Subject: [PATCH 18/65] *consolidate all expression constants to one function. --- src/codegen.rs | 52 +++++----- src/codegen/evaluator.rs | 147 ++++++++++++++++++++++++---- src/codegen/template.rs | 4 +- src/codegen/util.rs | 2 +- templates/Halo2VerifierReusable.sol | 7 +- templates/Halo2VerifyingKey.sol | 14 +-- 6 files changed, 165 insertions(+), 61 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 0d65824..6590840 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -272,7 +272,7 @@ impl<'a> SolidityGenerator<'a> { constants: constants.clone(), fixed_comms: fixed_comms.clone(), permutation_comms: permutation_comms.clone(), - const_lookup_input_expressions: vec![], + const_expressions: vec![], num_advices_user_challenges: vec![], gate_computations_lens: vec![], }; @@ -294,11 +294,11 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - let result: (Vec<(Vec, String)>, Vec) = - evaluator.lookup_computations(None, false); - - let const_lookup_input_expressions = - result.1.into_iter().map(fr_to_u256).collect::>(); + let const_expressions = evaluator + .expression_consts() + .into_iter() + .map(fr_to_u256) + .collect::>(); let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); @@ -342,9 +342,17 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); - let gate_computations_len_offset = (constants.len() * 0x20) + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 - + (const_lookup_input_expressions.len() * 0x20) + + (const_expressions.len() * 0x20); + + // set the num_advices_user_challenges_offset at position 4 + constants[4] = ( + "num_advices_user_challenges_offset", + U256::from(num_advices_user_challenges_offset), + ); + + let gate_computations_len_offset = num_advices_user_challenges_offset + ((num_advices_user_challenges.len() * 0x40) + 0x20); // set the gate_computations_len_offset at position 28. @@ -353,21 +361,13 @@ impl<'a> SolidityGenerator<'a> { U256::from(gate_computations_len_offset), ); - let num_advices_user_challenges_offset = (constants.len() * 0x20) - + (fixed_comms.len() + permutation_comms.len()) * 0x40 - + (const_lookup_input_expressions.len() * 0x20); - - // set the num_advices_user_challenges_offset at position 4 - constants[4] = ( - "num_advices_user_challenges_offset", - U256::from(num_advices_user_challenges_offset), - ); + // Collect the gate computations expressions let mut vk = Halo2VerifyingKey { constants, fixed_comms, permutation_comms, - const_lookup_input_expressions, + const_expressions, num_advices_user_challenges, gate_computations_lens, }; @@ -397,7 +397,7 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), evaluator.permutation_computations(false), - evaluator.lookup_computations(None, false).0 + evaluator.lookup_computations(None, false) ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -444,20 +444,20 @@ impl<'a> SolidityGenerator<'a> { let vk = self.generate_vk(true); let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); let vk_mptr = Ptr::memory(vk_m); - // if separate then create a hashmap of vk.const_lookup_input_expressions values to its vk memory location. + // if separate then create a hashmap of vk.const_expressions values to its vk memory location. let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); - // create hashmap of vk.const_lookup_input_expressions values to its vk memory location. + // create hashmap of vk.const_expressions values to its vk memory location. let offset = vk_m + (vk.constants.len() * 0x20) + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; - // keys to the map are the values of vk.const_lookup_input_expressions and values are the memory location of the vk.const_lookup_input_expressions. - vk.const_lookup_input_expressions + // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. + vk.const_expressions .iter() .enumerate() .for_each(|(idx, _)| { let mptr = offset + (0x20 * idx); let mptr = Ptr::memory(mptr); - vk_lookup_const_table.insert(vk.const_lookup_input_expressions[idx], mptr); + vk_lookup_const_table.insert(vk.const_expressions[idx], mptr); }); let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); @@ -466,9 +466,7 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), evaluator.permutation_computations(true), - evaluator - .lookup_computations(Some(vk_lookup_const_table), true) - .0 + evaluator.lookup_computations(Some(vk_lookup_const_table), true) ] .enumerate() .map(|(idx, (mut lines, var))| { diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index bd3681d..6ae222b 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -24,6 +24,20 @@ pub(crate) struct Evaluator<'a, F: PrimeField> { var_cache: RefCell>, } +// // Define an enum which catagorizes the operand memory location: +// // calldata_mptr +// // constant_mptr +// // instance_mptr +// // chllenge_mptr +// // static_memory_ptr +// #[derive(Clone, PartialEq, Eq)] +// pub enum OperandMem { +// Calldata, +// Constant, +// Instance, +// Challenge, +// StaticMemory, +// } impl<'a, F> Evaluator<'a, F> where F: PrimeField, @@ -42,6 +56,34 @@ where } } + pub fn expression_consts(&self) -> Vec { + let mut inputs_consts: Vec = Vec::new(); + self.cs + .gates() + .iter() + .flat_map(Gate::polynomials) + .for_each(|expression| self.collect_constants(expression, &mut inputs_consts)); + let evaluate_lookup_consts = |expressions: &Vec<_>, constants: &mut Vec| { + expressions.iter().for_each(|expression| { + self.collect_constants(expression, constants); + }); + }; + self.cs.lookups().iter().for_each(|lookup| { + let expressions: &Vec>> = lookup.input_expressions(); + expressions.iter().for_each(|arg| { + evaluate_lookup_consts(arg, &mut inputs_consts); + }); + }); + // Remove duplicates while preserving order + let mut unique_inputs_consts = Vec::new(); + for const_value in inputs_consts.clone() { + if !unique_inputs_consts.contains(&const_value) { + unique_inputs_consts.push(const_value); + } + } + unique_inputs_consts + } + pub fn gate_computations(&self) -> Vec<(Vec, String)> { self.cs .gates() @@ -51,6 +93,15 @@ where .collect() } + // pub fn gate_computation_separate_vk(&self) -> Vec> { + // self.cs + // .gates() + // .iter() + // .flat_map(Gate::polynomials) + // .map(|expression| self.evaluate_and_reset(expression)) + // .collect() + // } + pub fn permutation_computations(&self, separate: bool) -> Vec<(Vec, String)> { let Self { meta, data, .. } = self; let last_chunk_idx = meta.num_permutation_zs - 1; @@ -135,7 +186,7 @@ where &self, vk_lookup_const_table: Option, super::util::Ptr>>, separate: bool, - ) -> (Vec<(Vec, String)>, Vec) { + ) -> Vec<(Vec, String)> { let evaluate = |expressions: &Vec<_>| { // println!("expressions: {:?}", expressions); let (lines, inputs) = expressions @@ -150,13 +201,6 @@ where (lines, inputs) }; - let evaluate_lookup_consts = |expressions: &Vec<_>, constants: &mut Vec| { - expressions.iter().for_each(|expression| { - self.collect_constants(expression, constants); - }); - }; - - let mut inputs_consts: Vec = Vec::new(); let inputs_tables = self .cs .lookups() @@ -164,20 +208,10 @@ where .map(|lookup| { let inputs_iter = lookup.input_expressions().iter(); let inputs = inputs_iter.clone().map(evaluate).collect_vec(); - inputs_iter.for_each(|arg| { - evaluate_lookup_consts(arg, &mut inputs_consts); - }); let table = evaluate(lookup.table_expressions()); (inputs, table) }) .collect_vec(); - // Remove duplicates while preserving order - let mut unique_inputs_consts = Vec::new(); - for const_value in inputs_consts.clone() { - if !unique_inputs_consts.contains(&const_value) { - unique_inputs_consts.push(const_value); - } - } let lookup_const_table = if let Some(vk_lookup_const_table) = vk_lookup_const_table { // map all the keys to u256_string let vk_lookup_const_table: HashMap = vk_lookup_const_table @@ -328,7 +362,7 @@ where }) .zip(iter::repeat("eval".to_string())) .collect_vec(); - (vec, unique_inputs_consts) + vec } #[cfg(not(feature = "mv-lookup"))] @@ -336,7 +370,7 @@ where &self, _vk_lookup_const_table: Option, super::util::Ptr>>, _separate: bool, - ) -> (Vec<(Vec, String)>, Vec) { + ) -> Vec<(Vec, String)> { let _ = |expressions: &Vec<_>, constants: &mut Vec| { expressions.iter().for_each(|expression| { self.collect_constants(expression, constants); @@ -438,7 +472,7 @@ where }) .zip(iter::repeat("eval".to_string())) .collect_vec(); - (vec, Vec::new()) + vec } fn eval(&self, column_type: impl Into, column_index: usize, rotation: i32) -> String { @@ -460,6 +494,58 @@ where result } + // fn evaluate_and_encode(&self, expression: &Expression) -> Vec { + // evaluate( + // expression, + // &|constant| { + // let constant = u256_string(constant); + // self.init_encoded_var(constant, OperandMem::Constant) + // }, + // &|query| { + // self.init_var( + // self.eval(Fixed, query.column_index(), query.rotation().0), + // Some(fixed_eval_var(query)), + // ) + // }, + // &|query| { + // self.init_var( + // self.eval(Advice::default(), query.column_index(), query.rotation().0), + // Some(advice_eval_var(query)), + // ) + // }, + // &|_| self.init_var(self.data.instance_eval, Some("i_eval".to_string())), + // &|challenge| { + // self.init_var( + // self.data.challenges[challenge.index()], + // Some(format!("c_{}", challenge.index())), + // ) + // }, + // &|(mut acc, var)| { + // let (lines, var) = self.init_var(format!("sub(r, {var})"), None); + // acc.extend(lines); + // (acc, var) + // }, + // &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { + // let (lines, var) = self.init_var(format!("addmod({lhs_var}, {rhs_var}, r)"), None); + // lhs_acc.extend(rhs_acc); + // lhs_acc.extend(lines); + // (lhs_acc, var) + // }, + // &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { + // let (lines, var) = self.init_var(format!("mulmod({lhs_var}, {rhs_var}, r)"), None); + // lhs_acc.extend(rhs_acc); + // lhs_acc.extend(lines); + // (lhs_acc, var) + // }, + // &|(mut acc, var), scalar| { + // let scalar = u256_string(scalar); + // let (lines, var) = self.init_var(format!("mulmod({var}, {scalar}, r)"), None); + // acc.extend(lines); + // (acc, var) + // }, + // ) + // } + #[allow(clippy::only_used_in_recursion)] #[allow(dead_code)] fn collect_constants(&self, expression: &Expression, constants: &mut Vec) { @@ -555,6 +641,25 @@ where *self.var_counter.borrow_mut() += 1; format!("var{count}") } + + // Return the encoded word and the static memory pointer + // fn init_encoded_var( + // &self, + // value: impl ToString, + // var: OperandMem, + // static_mem_ptr: usize, + // ) -> (Vec, usize) { + // let value = value.to_string(); + // if self.var_cache.borrow().contains_key(&value) { + // (vec![], self.var_cache.borrow()[&value].clone()) + // } else { + // let var = var.unwrap_or_else(|| self.next_var()); + // self.var_cache + // .borrow_mut() + // .insert(value.clone(), var.clone()); + // (vec![format!("let {var} := {value}")], var) + // } + // } } fn u256_string(value: U256) -> String { diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 0469480..2338468 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -13,7 +13,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) num_advices_user_challenges: Vec<(U256, U256)>, pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, - pub(crate) const_lookup_input_expressions: Vec, + pub(crate) const_expressions: Vec, pub(crate) gate_computations_lens: Vec, } @@ -21,7 +21,7 @@ impl Halo2VerifyingKey { pub(crate) fn len(&self) -> usize { (self.constants.len() * 0x20) + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 - + (self.const_lookup_input_expressions.len() * 0x20) + + (self.const_expressions.len() * 0x20) + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) + (self.gate_computations_lens.len() * 0x20 + 0x20) } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 3a7a2fa..cddcf67 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -400,7 +400,7 @@ impl Data { let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); let challenge_mptr = permutation_comm_mptr + (2 * vk.permutation_comms.len()) - + vk.const_lookup_input_expressions.len() + + vk.const_expressions.len() + (2 * vk.num_advices_user_challenges.len() + 1) + (vk.gate_computations_lens.len() + 1); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 2fe15fa..8f70a8d 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -406,15 +406,16 @@ contract Halo2Verifier { // { // let quotient_eval_numer // let y := mload(add(theta_mptr, 0x60)) - // let code_blocks_len := mload(add(vk_mptr, 0x380)) // Remember this length represented in bytes - // let code_blocks_len_offset := mload(add(vk_mptr, CODE_BLOCKS_LENS_OFFSET)) // TODO fill in the correct offset + // let code_blocks_offset_ptr := add(vk_mptr,(add(vk_mptr, 0x380))) + // let code_blocks_lens_len := mload(code_blocks_offset_ptr) // Remember this length represented in bytes + // let code_blocks_len_offset := mload(add(code_blocks_lens_len, 0x20)) // let expressions_ptr := add(vk_mptr, EXPRESSIONS_OFFSET) // TODO fill in the correct offset // let expression := 0x0 // Initialize this to 0. Will set it later in the loop // let experssion_words_counter := 0 // let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks // let constants_ptr := add(vk_mptr, CONSTANTS_OFFSET) // TODO fill in the correct offset // // Load in the total number of code blocks from the vk constants, right after the number challenges - // for { let code_block := 0 } lt(code_block, code_blocks_len) { code_block := add(code_block, 0x20) } { + // for { let code_block := 0 } lt(code_block, code_blocks_lens_len) { code_block := add(code_block, 0x20) } { // // Shift the code_len by the free_static_memory_ptr // let code_len := add(mload(add(code_blocks_len_offset, code_block)), free_static_memory_ptr) // // loop through code len diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 8a777b2..98daa5d 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -18,24 +18,24 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} - {%- for const in const_lookup_input_expressions %} + {%- for const in const_expressions %} {%- let offset =constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_lookup_input_expressions[{{ loop.index0 }}] + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len() %} + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // num_advices_user_challenges length {%- for (x, y) in num_advices_user_challenges %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_lookup_input_expressions.len() + 1 %} + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() + 1 %} mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // num_advices[{{ loop.index0 }}].x mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 1 %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // gate_computations_lens length {%- for gate_computations_len in gate_computations_lens %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 2 %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ gate_computations_len|hex_padded(64) }}) // gate_computations_len[{{ loop.index0 }}] {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_lookup_input_expressions.len() + 2 + gate_computations_lens.len()))|hex() }}) + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations_lens.len()))|hex() }}) } } } From 591389d0ad29e4a44b3a013c267cbbed2f1a14bc Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 11 Jul 2024 00:17:59 -0500 Subject: [PATCH 19/65] *layout encoded gate computation expressions in VK. --- src/codegen.rs | 102 ++++++-- src/codegen/evaluator.rs | 379 +++++++++++++++++----------- src/codegen/template.rs | 8 +- src/codegen/util.rs | 60 ++++- templates/Halo2VerifierReusable.sol | 21 +- templates/Halo2VerifyingKey.sol | 17 +- 6 files changed, 394 insertions(+), 193 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 6590840..52b5b2f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,11 +1,13 @@ use crate::codegen::{ - evaluator::Evaluator, + evaluator::{Evaluator, EvaluatorVK}, pcs::{ bdfg21_computations, queries, rotation_sets, BatchOpenScheme::{Bdfg21, Gwc19}, }, template::{Halo2Verifier, Halo2VerifyingKey}, - util::{fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr}, + util::{ + expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, + }, }; use halo2_proofs::{ halo2curves::{bn256, ff::Field}, @@ -274,32 +276,44 @@ impl<'a> SolidityGenerator<'a> { permutation_comms: permutation_comms.clone(), const_expressions: vec![], num_advices_user_challenges: vec![], - gate_computations_lens: vec![], + gate_computations: vec![], + gate_computations_total_length: 0, }; if !separate { return attached_vk; } + // Pass a vk.len() to estimate_static_working_memory. - let vk_mptr_mock = Ptr::memory( - self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), - ); + let vk_mptr_mock = + self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); let data = Data::new( &self.meta, &attached_vk, - vk_mptr_mock, + Ptr::memory(vk_mptr_mock), Ptr::calldata(0x84), true, ); - let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - - let const_expressions = evaluator - .expression_consts() + let const_expressions = expression_consts(self.vk.cs()) .into_iter() .map(fr_to_u256) .collect::>(); + let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); + let offset = vk_mptr_mock + + (attached_vk.constants.len() * 0x20) + + (attached_vk.fixed_comms.len() + attached_vk.permutation_comms.len()) * 0x40; + // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. + const_expressions.iter().enumerate().for_each(|(idx, _)| { + let mptr = offset + (0x20 * idx); + let mptr = Ptr::memory(mptr); + vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); + }); + + let evaluator_dummy = + EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table_dummy); + let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); // set instance_cptr it at position 7 of the constants. @@ -321,10 +335,6 @@ impl<'a> SolidityGenerator<'a> { U256::from(last_quotient_x_cptr.value().as_usize()), ); - let gate_computations_lens: Vec = chain![evaluator.gate_computations()] - .map(|(lines, _)| U256::from(lines.len())) - .collect(); - let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); // truncate the last elements of num_user_challenges to match the length of num_advices. @@ -342,6 +352,19 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); + // Fill in the gate computations with dummy values. + let mut cumulative_length = 0; + let dummy_gate_computations: Vec<(Vec, usize)> = + chain![evaluator_dummy.gate_computations()] + .map(|(lines, _)| { + let operations = lines.iter().map(|_line| U256::from(0)).collect::>(); + let length = operations.len(); + let gate_computation = (operations, cumulative_length); + cumulative_length += length; + gate_computation + }) + .collect(); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_expressions.len() * 0x20); @@ -361,25 +384,58 @@ impl<'a> SolidityGenerator<'a> { U256::from(gate_computations_len_offset), ); - // Collect the gate computations expressions - let mut vk = Halo2VerifyingKey { constants, fixed_comms, permutation_comms, const_expressions, num_advices_user_challenges, - gate_computations_lens, + gate_computations: dummy_gate_computations, + gate_computations_total_length: cumulative_length, }; - // new generate the real vk_mptr - let vk_mptr = Ptr::memory( - self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)), - ); + // Now generate the real vk_mptr with a vk that has the correct length + let vk_mptr = self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); // replace the mock vk_mptr with the real vk_mptr - vk.constants[1] = ("vk_mptr", U256::from(vk_mptr.value().as_usize())); + vk.constants[1] = ("vk_mptr", U256::from(vk_mptr)); // replace the mock vk_len with the real vk_len let vk_len = vk.len(); vk.constants[2] = ("vk_len", U256::from(vk_len)); + + // Regenerate the gate computations with the correct offsets. + let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); + // create hashmap of vk.const_expressions values to its vk memory location. + let offset = vk_mptr + + (vk.constants.len() * 0x20) + + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; + // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. + vk.const_expressions + .iter() + .enumerate() + .for_each(|(idx, _)| { + let mptr = offset + (0x20 * idx); + let mptr = Ptr::memory(mptr); + vk_lookup_const_table.insert(vk.const_expressions[idx], mptr); + }); + + // Now we initalize the real evaluator_vk which will contain the correct offsets in the vk_lookup_const_table. + let evaluator = EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table); + + let mut cumulative_length = 0; + let gate_computations: Vec<(Vec, usize)> = chain![evaluator.gate_computations()] + .map(|(lines, _)| { + let operations = lines + .iter() + .map(|line: &ruint::Uint<256, 4>| U256::from(*line)) + .collect::>(); + let length = operations.len() * 0x20; + let gate_computation = (operations, cumulative_length); + cumulative_length += length; + gate_computation + }) + .collect(); + + vk.gate_computations = gate_computations; + // NOTE: We don't need to replace the gate_computations_total_length since we are only potentially modifying the offsets for each constant mload operation. vk } diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 6ae222b..59917d2 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -13,7 +13,7 @@ use regex::Regex; use ruint::aliases::U256; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; -use super::util::get_memory_ptr; +use super::util::{get_memory_ptr, Ptr}; #[derive(Debug)] pub(crate) struct Evaluator<'a, F: PrimeField> { @@ -24,20 +24,6 @@ pub(crate) struct Evaluator<'a, F: PrimeField> { var_cache: RefCell>, } -// // Define an enum which catagorizes the operand memory location: -// // calldata_mptr -// // constant_mptr -// // instance_mptr -// // chllenge_mptr -// // static_memory_ptr -// #[derive(Clone, PartialEq, Eq)] -// pub enum OperandMem { -// Calldata, -// Constant, -// Instance, -// Challenge, -// StaticMemory, -// } impl<'a, F> Evaluator<'a, F> where F: PrimeField, @@ -56,34 +42,6 @@ where } } - pub fn expression_consts(&self) -> Vec { - let mut inputs_consts: Vec = Vec::new(); - self.cs - .gates() - .iter() - .flat_map(Gate::polynomials) - .for_each(|expression| self.collect_constants(expression, &mut inputs_consts)); - let evaluate_lookup_consts = |expressions: &Vec<_>, constants: &mut Vec| { - expressions.iter().for_each(|expression| { - self.collect_constants(expression, constants); - }); - }; - self.cs.lookups().iter().for_each(|lookup| { - let expressions: &Vec>> = lookup.input_expressions(); - expressions.iter().for_each(|arg| { - evaluate_lookup_consts(arg, &mut inputs_consts); - }); - }); - // Remove duplicates while preserving order - let mut unique_inputs_consts = Vec::new(); - for const_value in inputs_consts.clone() { - if !unique_inputs_consts.contains(&const_value) { - unique_inputs_consts.push(const_value); - } - } - unique_inputs_consts - } - pub fn gate_computations(&self) -> Vec<(Vec, String)> { self.cs .gates() @@ -93,15 +51,6 @@ where .collect() } - // pub fn gate_computation_separate_vk(&self) -> Vec> { - // self.cs - // .gates() - // .iter() - // .flat_map(Gate::polynomials) - // .map(|expression| self.evaluate_and_reset(expression)) - // .collect() - // } - pub fn permutation_computations(&self, separate: bool) -> Vec<(Vec, String)> { let Self { meta, data, .. } = self; let last_chunk_idx = meta.num_permutation_zs - 1; @@ -371,11 +320,6 @@ where _vk_lookup_const_table: Option, super::util::Ptr>>, _separate: bool, ) -> Vec<(Vec, String)> { - let _ = |expressions: &Vec<_>, constants: &mut Vec| { - expressions.iter().for_each(|expression| { - self.collect_constants(expression, constants); - }); - }; let input_tables = self .cs .lookups() @@ -494,83 +438,6 @@ where result } - // fn evaluate_and_encode(&self, expression: &Expression) -> Vec { - // evaluate( - // expression, - // &|constant| { - // let constant = u256_string(constant); - // self.init_encoded_var(constant, OperandMem::Constant) - // }, - // &|query| { - // self.init_var( - // self.eval(Fixed, query.column_index(), query.rotation().0), - // Some(fixed_eval_var(query)), - // ) - // }, - // &|query| { - // self.init_var( - // self.eval(Advice::default(), query.column_index(), query.rotation().0), - // Some(advice_eval_var(query)), - // ) - // }, - // &|_| self.init_var(self.data.instance_eval, Some("i_eval".to_string())), - // &|challenge| { - // self.init_var( - // self.data.challenges[challenge.index()], - // Some(format!("c_{}", challenge.index())), - // ) - // }, - // &|(mut acc, var)| { - // let (lines, var) = self.init_var(format!("sub(r, {var})"), None); - // acc.extend(lines); - // (acc, var) - // }, - // &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { - // let (lines, var) = self.init_var(format!("addmod({lhs_var}, {rhs_var}, r)"), None); - // lhs_acc.extend(rhs_acc); - // lhs_acc.extend(lines); - // (lhs_acc, var) - // }, - // &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { - // let (lines, var) = self.init_var(format!("mulmod({lhs_var}, {rhs_var}, r)"), None); - // lhs_acc.extend(rhs_acc); - // lhs_acc.extend(lines); - // (lhs_acc, var) - // }, - // &|(mut acc, var), scalar| { - // let scalar = u256_string(scalar); - // let (lines, var) = self.init_var(format!("mulmod({var}, {scalar}, r)"), None); - // acc.extend(lines); - // (acc, var) - // }, - // ) - // } - - #[allow(clippy::only_used_in_recursion)] - #[allow(dead_code)] - fn collect_constants(&self, expression: &Expression, constants: &mut Vec) { - match expression { - Expression::Constant(constant) => { - constants.push(*constant); - } - Expression::Negated(inner) => { - self.collect_constants(inner, constants); - } - Expression::Sum(lhs, rhs) => { - self.collect_constants(lhs, constants); - self.collect_constants(rhs, constants); - } - Expression::Product(lhs, rhs) => { - self.collect_constants(lhs, constants); - self.collect_constants(rhs, constants); - } - Expression::Scaled(inner, _scalar) => { - self.collect_constants(inner, constants); - } - _ => {} - } - } - fn evaluate(&self, expression: &Expression) -> (Vec, String) { evaluate( expression, @@ -641,25 +508,237 @@ where *self.var_counter.borrow_mut() += 1; format!("var{count}") } +} - // Return the encoded word and the static memory pointer - // fn init_encoded_var( +#[derive(Debug)] +pub(crate) struct EvaluatorVK<'a, F: PrimeField> { + cs: &'a ConstraintSystem, + #[allow(dead_code)] + meta: &'a ConstraintSystemMeta, + data: &'a Data, + static_mem_ptr: RefCell, + encoded_var_cache: RefCell>, + const_cache: RefCell, Ptr>>, +} + +// // Define an enum which catagorizes the operand memory location: +// // calldata_mptr +// // constant_mptr +// // instance_mptr +// // chllenge_mptr +// // static_memory_ptr +#[derive(Clone, PartialEq, Eq)] +pub enum OperandMem { + Calldata, + Constant, + Instance, + Challenge, + StaticMemory, +} + +impl<'a, F> EvaluatorVK<'a, F> +where + F: PrimeField, +{ + pub(crate) fn new( + cs: &'a ConstraintSystem, + meta: &'a ConstraintSystemMeta, + data: &'a Data, + const_cache: HashMap, Ptr>, + ) -> Self { + Self { + cs, + meta, + data, + static_mem_ptr: RefCell::new(0x20), + encoded_var_cache: Default::default(), + const_cache: RefCell::new(const_cache), + } + } + + pub fn gate_computations(&self) -> Vec<(Vec, U256)> { + self.cs + .gates() + .iter() + .flat_map(Gate::polynomials) + .map(|expression| self.evaluate_and_reset(expression)) + .collect() + } + + // pub fn permutation_computations(&self, _separate: bool) -> Vec<(Vec, String)> { + // todo!() + // } + + // #[cfg(feature = "mv-lookup")] + // pub fn lookup_computations( // &self, - // value: impl ToString, - // var: OperandMem, - // static_mem_ptr: usize, - // ) -> (Vec, usize) { - // let value = value.to_string(); - // if self.var_cache.borrow().contains_key(&value) { - // (vec![], self.var_cache.borrow()[&value].clone()) - // } else { - // let var = var.unwrap_or_else(|| self.next_var()); - // self.var_cache - // .borrow_mut() - // .insert(value.clone(), var.clone()); - // (vec![format!("let {var} := {value}")], var) - // } + // _vk_lookup_const_table: Option, super::util::Ptr>>, + // _separate: bool, + // ) -> Vec<(Vec, String)> { + // todo!() // } + + // #[cfg(not(feature = "mv-lookup"))] + // pub fn lookup_computations( + // &self, + // _vk_lookup_const_table: Option, super::util::Ptr>>, + // _separate: bool, + // ) -> Vec<(Vec, String)> { + // todo!() + // } + + fn eval_encoded( + &self, + column_type: impl Into, + column_index: usize, + rotation: i32, + ) -> U256 { + match column_type.into() { + Any::Advice(_) => self.encode_single_operand( + 0_u8, + U256::from( + self.data.advice_evals[&(column_index, rotation)] + .ptr() + .value() + .as_usize(), + ), + ), + Any::Fixed => self.encode_single_operand( + 0_u8, + U256::from( + self.data.fixed_evals[&(column_index, rotation)] + .ptr() + .value() + .as_usize(), + ), + ), + Any::Instance => unimplemented!(), + } + } + + fn reset(&self) { + *self.static_mem_ptr.borrow_mut() = Default::default(); + *self.encoded_var_cache.borrow_mut() = Default::default(); + *self.const_cache.borrow_mut() = Default::default(); + } + + fn encode_operation(&self, op: u8, lhs_ptr: U256, rhs_ptr: U256) -> U256 { + U256::from(op) | (lhs_ptr << 8) | (rhs_ptr << 24) + } + + fn encode_single_operand(&self, op: u8, ptr: U256) -> U256 { + U256::from(op) | (ptr << 8) + } + + fn evaluate_and_reset(&self, expression: &Expression) -> (Vec, U256) { + let result = self.evaluate_encode(expression); + self.reset(); + result + } + + fn evaluate_encode(&self, expression: &Expression) -> (Vec, U256) { + evaluate( + expression, + &|constant| self.init_encoded_var(constant, OperandMem::Constant), + &|query| { + self.init_encoded_var( + self.eval_encoded(Fixed, query.column_index(), query.rotation().0), + OperandMem::Calldata, + ) + }, + &|query| { + self.init_encoded_var( + self.eval_encoded(Advice::default(), query.column_index(), query.rotation().0), + OperandMem::Calldata, + ) + }, + &|_| { + self.init_encoded_var( + U256::from(self.data.instance_eval.ptr().value().as_usize()), + OperandMem::Instance, + ) + }, + &|challenge| { + self.init_encoded_var( + U256::from( + self.data.challenges[challenge.index()] + .ptr() + .value() + .as_usize(), + ), + OperandMem::Challenge, + ) + }, + &|(mut acc, var)| { + let (lines, var) = self.init_encoded_var( + self.encode_single_operand(1_u8, var), + OperandMem::StaticMemory, + ); + acc.extend(lines); + (acc, var) + }, + &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { + let (lines, var) = self.init_encoded_var( + self.encode_operation(2_u8, lhs_var, rhs_var), + OperandMem::StaticMemory, + ); + lhs_acc.extend(rhs_acc); + lhs_acc.extend(lines); + (lhs_acc, var) + }, + &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { + let (lines, var) = self.init_encoded_var( + self.encode_operation(3_u8, lhs_var, rhs_var), + OperandMem::StaticMemory, + ); + lhs_acc.extend(rhs_acc); + lhs_acc.extend(lines); + (lhs_acc, var) + }, + &|(mut acc, var), scalar| { + // fetch the scalar pointer from the const cache + let scalar_ptr = self.const_cache.borrow()[&scalar]; + let (lines, var) = self.init_encoded_var( + self.encode_operation(3_u8, var, U256::from(scalar_ptr.value().as_usize())), + OperandMem::StaticMemory, + ); + acc.extend(lines); + (acc, var) + }, + ) + } + + // Return the encoded word and the static memory pointer + fn init_encoded_var(&self, value: U256, var: OperandMem) -> (Vec, U256) { + match var { + OperandMem::Calldata | OperandMem::StaticMemory => { + if self.encoded_var_cache.borrow().contains_key(&value) { + (vec![], self.encoded_var_cache.borrow()[&value]) + } else { + let var = self.next_encoded_var(); + self.encoded_var_cache.borrow_mut().insert(value, var); + (vec![value], var) + } + } + OperandMem::Constant => ( + vec![], + U256::from(self.const_cache.borrow().get(&value).map_or_else( + || { + println!("Key not found: {}", value); + 0 // Default value, you can change this if needed + }, + |entry| entry.value().as_usize(), + )), + ), + OperandMem::Instance | OperandMem::Challenge => (vec![], value), + } + } + + fn next_encoded_var(&self) -> U256 { + let count = *self.static_mem_ptr.borrow(); + *self.static_mem_ptr.borrow_mut() += 0x20; + U256::from(count) + } } fn u256_string(value: U256) -> String { diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 2338468..ecf894f 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -14,7 +14,8 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_expressions: Vec, - pub(crate) gate_computations_lens: Vec, + pub(crate) gate_computations: Vec<(Vec,usize)>, + pub(crate) gate_computations_total_length: usize } impl Halo2VerifyingKey { @@ -23,7 +24,10 @@ impl Halo2VerifyingKey { + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + (self.const_expressions.len() * 0x20) + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) - + (self.gate_computations_lens.len() * 0x20 + 0x20) + // The length words of the inner vector + length word of the outer vector + + (self.gate_computations.len() * 0x20 + 0x20) + // Sum up the lengths of al the nested vectors + + (self.gate_computations_total_length * 0x20) } } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index cddcf67..f37f5ba 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -4,7 +4,7 @@ use crate::codegen::{ }; use halo2_proofs::{ halo2curves::{bn256, ff::PrimeField, CurveAffine}, - plonk::{Any, Column, ConstraintSystem}, + plonk::{Any, Column, ConstraintSystem, Expression, Gate}, }; use itertools::{chain, izip, Itertools}; use ruint::{aliases::U256, UintTryFrom}; @@ -402,7 +402,8 @@ impl Data { + (2 * vk.permutation_comms.len()) + vk.const_expressions.len() + (2 * vk.num_advices_user_challenges.len() + 1) - + (vk.gate_computations_lens.len() + 1); + + (vk.gate_computations.len() + 1) + + (vk.gate_computations_total_length); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; @@ -939,6 +940,61 @@ where U256::from_le_bytes(fe.borrow().to_repr()) } +pub(crate) fn expression_consts(cs: &ConstraintSystem) -> Vec +where + F: PrimeField, +{ + fn collect_constants(expression: &Expression, constants: &mut Vec) { + match expression { + Expression::Constant(constant) => { + constants.push(*constant); + } + Expression::Negated(inner) => { + collect_constants(inner, constants); + } + Expression::Sum(lhs, rhs) => { + collect_constants(lhs, constants); + collect_constants(rhs, constants); + } + Expression::Product(lhs, rhs) => { + collect_constants(lhs, constants); + collect_constants(rhs, constants); + } + Expression::Scaled(inner, scalar) => { + collect_constants(inner, constants); + // we consider scalar values constants + constants.push(*scalar); + } + _ => {} + } + } + + let mut inputs_consts: Vec = Vec::new(); + cs.gates() + .iter() + .flat_map(Gate::polynomials) + .for_each(|expression| collect_constants(expression, &mut inputs_consts)); + let evaluate_lookup_consts = |expressions: &Vec<_>, constants: &mut Vec| { + expressions.iter().for_each(|expression| { + collect_constants(expression, constants); + }); + }; + cs.lookups().iter().for_each(|lookup| { + let expressions: &Vec>> = lookup.input_expressions(); + expressions.iter().for_each(|arg| { + evaluate_lookup_consts(arg, &mut inputs_consts); + }); + }); + // Remove duplicates while preserving order + let mut unique_inputs_consts = Vec::new(); + for const_value in inputs_consts.clone() { + if !unique_inputs_consts.contains(&const_value) { + unique_inputs_consts.push(const_value); + } + } + unique_inputs_consts +} + pub(crate) fn to_u256_be_bytes(value: T) -> [u8; 32] where U256: UintTryFrom, diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 8f70a8d..4555f97 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -406,23 +406,24 @@ contract Halo2Verifier { // { // let quotient_eval_numer // let y := mload(add(theta_mptr, 0x60)) - // let code_blocks_offset_ptr := add(vk_mptr,(add(vk_mptr, 0x380))) - // let code_blocks_lens_len := mload(code_blocks_offset_ptr) // Remember this length represented in bytes - // let code_blocks_len_offset := mload(add(code_blocks_lens_len, 0x20)) + // let gate_computations_ptr := add(vk_mptr,(add(vk_mptr, 0x380))) + // let gate_computations_len := mload(gate_computations_ptr) // Remember this length represented in bytes + // let gate_computations := mload(add(gate_computations_ptr, 0x20)) // let expressions_ptr := add(vk_mptr, EXPRESSIONS_OFFSET) // TODO fill in the correct offset // let expression := 0x0 // Initialize this to 0. Will set it later in the loop - // let experssion_words_counter := 0 + // let expression_acc := 0 // let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks // let constants_ptr := add(vk_mptr, CONSTANTS_OFFSET) // TODO fill in the correct offset // // Load in the total number of code blocks from the vk constants, right after the number challenges - // for { let code_block := 0 } lt(code_block, code_blocks_lens_len) { code_block := add(code_block, 0x20) } { + // for { let code_block := 0 } lt(code_block, gate_computations_len) { code_block := add(code_block, 0x20) } { + // let code_ptr := add(add(gate_computations, code_block), expression_acc) // // Shift the code_len by the free_static_memory_ptr - // let code_len := add(mload(add(code_blocks_len_offset, code_block)), free_static_memory_ptr) + // let code_len := add(mload(code_ptr), free_static_memory_ptr) // // loop through code len // for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { // /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word - // expression := mload(add(expressions_ptr, experssion_words_counter)) - // experssion_words_counter := add(experssion_words_counter, 0x20) + // expression := mload(add(code_ptr, i)) + // expression_acc := add(expression_acc, 0x20) // // Load in the least significant byte located of the `expression` word to get the operation type // let byte := and(expression, 0xFF) @@ -442,13 +443,13 @@ contract Halo2Verifier { // else if (eq(byte, 0x02)) { // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - // mstore(i,addmod(mload(and(shr(24, expressions), 0xFFFF)),mload(and(shr(8, expressions), 0xFFFF)),r)) + // mstore(i,addmod(mload(and(shr(8, expressions), 0xFFFF)),mload(and(shr(24, expressions), 0xFFFF)),r)) // } // // 0x03 => Product/scalar expression // else if (eq(byte, 0x03)) { // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - // mstore(i,mulmod(mload(and(shr(24, expressions), 0xFFFF)),mload(and(shr(8, expressions), 0xFFFF)),r)) + // mstore(i,mulmod(mload(and(shr(8, expressions), 0xFFFF)),mload(and(shr(24, expressions), 0xFFFF)),r)) // } // } // // at the end of each code block we update `quotient_eval_numer` diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 98daa5d..8faf834 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -19,7 +19,7 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} {%- for const in const_expressions %} - {%- let offset =constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} + {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() %} @@ -30,12 +30,17 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // gate_computations_lens length - {%- for gate_computations_len in gate_computations_lens %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ gate_computations_len|hex_padded(64) }}) // gate_computations_len[{{ loop.index0 }}] + mstore({{ (32 * offset)|hex_padded(4) }}, {{ gate_computations.len()|hex_padded(64) }}) // gate_computations length + {%- for (gate_computation, acc) in gate_computations %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} + {%- let offset = base_offset + loop.index0 + acc %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ gate_computation.len()|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] + {%- for operation in gate_computation %} + {%- let offset = offset + loop.index0 + 1 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ operation|hex_padded(64) }}) // gate_computation[{{ loop.index0 }}] {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations_lens.len()))|hex() }}) + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length ))|hex() }}) } } } From 37945c185b14bff85fc204ce7db76a39995705f0 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 11 Jul 2024 15:22:31 -0500 Subject: [PATCH 20/65] *cfg feat not mv-lookup expression_consts. --- src/codegen/template.rs | 6 ++--- src/codegen/util.rs | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/codegen/template.rs b/src/codegen/template.rs index ecf894f..bcc3d4d 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -14,8 +14,8 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_expressions: Vec, - pub(crate) gate_computations: Vec<(Vec,usize)>, - pub(crate) gate_computations_total_length: usize + pub(crate) gate_computations: Vec<(Vec, usize)>, + pub(crate) gate_computations_total_length: usize, } impl Halo2VerifyingKey { @@ -25,7 +25,7 @@ impl Halo2VerifyingKey { + (self.const_expressions.len() * 0x20) + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) // The length words of the inner vector + length word of the outer vector - + (self.gate_computations.len() * 0x20 + 0x20) + + (self.gate_computations.len() * 0x20 + 0x20) // Sum up the lengths of al the nested vectors + (self.gate_computations_total_length * 0x20) } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index f37f5ba..3a401c2 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -940,6 +940,7 @@ where U256::from_le_bytes(fe.borrow().to_repr()) } +#[cfg(feature = "mv-lookup")] pub(crate) fn expression_consts(cs: &ConstraintSystem) -> Vec where F: PrimeField, @@ -995,6 +996,57 @@ where unique_inputs_consts } +#[cfg(not(feature = "mv-lookup"))] +pub(crate) fn expression_consts(cs: &ConstraintSystem) -> Vec +where + F: PrimeField, +{ + fn collect_constants(expression: &Expression, constants: &mut Vec) { + match expression { + Expression::Constant(constant) => { + constants.push(*constant); + } + Expression::Negated(inner) => { + collect_constants(inner, constants); + } + Expression::Sum(lhs, rhs) => { + collect_constants(lhs, constants); + collect_constants(rhs, constants); + } + Expression::Product(lhs, rhs) => { + collect_constants(lhs, constants); + collect_constants(rhs, constants); + } + Expression::Scaled(inner, scalar) => { + collect_constants(inner, constants); + // we consider scalar values constants + constants.push(*scalar); + } + _ => {} + } + } + + let mut inputs_consts: Vec = Vec::new(); + cs.gates() + .iter() + .flat_map(Gate::polynomials) + .for_each(|expression| collect_constants(expression, &mut inputs_consts)); + cs.lookups().iter().for_each(|lookup| { + let expressions: &Vec> = lookup.input_expressions(); + expressions.iter().for_each(|arg| { + collect_constants(arg, &mut inputs_consts); + }); + }); + // Remove duplicates while preserving order + let mut unique_inputs_consts = Vec::new(); + for const_value in inputs_consts.clone() { + if !unique_inputs_consts.contains(&const_value) { + unique_inputs_consts.push(const_value); + } + } + unique_inputs_consts +} + pub(crate) fn to_u256_be_bytes(value: T) -> [u8; 32] where U256: UintTryFrom, From 82d3033d5bcb8977a0259be9753da01f3f4b7b39 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 11 Jul 2024 16:06:55 -0500 Subject: [PATCH 21/65] *don't reset const cache between code blocks in gate computations. --- src/codegen.rs | 1 - src/codegen/evaluator.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 52b5b2f..222a847 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -310,7 +310,6 @@ impl<'a> SolidityGenerator<'a> { let mptr = Ptr::memory(mptr); vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); }); - let evaluator_dummy = EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table_dummy); diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 59917d2..701cd64 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -619,7 +619,6 @@ where fn reset(&self) { *self.static_mem_ptr.borrow_mut() = Default::default(); *self.encoded_var_cache.borrow_mut() = Default::default(); - *self.const_cache.borrow_mut() = Default::default(); } fn encode_operation(&self, op: u8, lhs_ptr: U256, rhs_ptr: U256) -> U256 { From f9149c7c90a0e14d276f6d87cd0e454c94e69e00 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 11 Jul 2024 22:43:22 -0500 Subject: [PATCH 22/65] *test gate_computation encoding in reusable verifier. --- src/codegen.rs | 34 ++-- src/codegen/evaluator.rs | 13 +- src/test.rs | 4 +- templates/Halo2Verifier.sol | 3 +- templates/Halo2VerifierReusable.sol | 254 ++++++++++------------------ templates/Halo2VerifyingKey.sol | 6 +- 6 files changed, 132 insertions(+), 182 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 222a847..57f7b9f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -283,11 +283,10 @@ impl<'a> SolidityGenerator<'a> { if !separate { return attached_vk; } - // Pass a vk.len() to estimate_static_working_memory. let vk_mptr_mock = self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); - let data = Data::new( + let dummy_data = Data::new( &self.meta, &attached_vk, Ptr::memory(vk_mptr_mock), @@ -310,15 +309,19 @@ impl<'a> SolidityGenerator<'a> { let mptr = Ptr::memory(mptr); vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); }); - let evaluator_dummy = - EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table_dummy); + let evaluator_dummy = EvaluatorVK::new( + self.vk.cs(), + &self.meta, + &dummy_data, + vk_lookup_const_table_dummy, + ); let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); // set instance_cptr it at position 7 of the constants. constants[7] = ("instance_cptr", instance_cptr); - let first_quotient_x_cptr = data.quotient_comm_cptr; + let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; // set first_quotient_x_cptr at position 6 of the constants. constants[6] = ( @@ -364,6 +367,8 @@ impl<'a> SolidityGenerator<'a> { }) .collect(); + println!("total length: {:?}", cumulative_length); + let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_expressions.len() * 0x20); @@ -393,7 +398,16 @@ impl<'a> SolidityGenerator<'a> { gate_computations_total_length: cumulative_length, }; // Now generate the real vk_mptr with a vk that has the correct length - let vk_mptr = self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); + let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84)); + + // Generte the real data. + let data = Data::new( + &self.meta, + &vk, + Ptr::memory(vk_mptr), + Ptr::calldata(0x84), + true, + ); // replace the mock vk_mptr with the real vk_mptr vk.constants[1] = ("vk_mptr", U256::from(vk_mptr)); // replace the mock vk_len with the real vk_len @@ -426,7 +440,7 @@ impl<'a> SolidityGenerator<'a> { .iter() .map(|line: &ruint::Uint<256, 4>| U256::from(*line)) .collect::>(); - let length = operations.len() * 0x20; + let length = operations.len(); let gate_computation = (operations, cumulative_length); cumulative_length += length; gate_computation @@ -520,8 +534,8 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), - evaluator.permutation_computations(true), - evaluator.lookup_computations(Some(vk_lookup_const_table), true) + // evaluator.permutation_computations(true), + // evaluator.lookup_computations(Some(vk_lookup_const_table), true) ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -529,7 +543,7 @@ impl<'a> SolidityGenerator<'a> { format!("quotient_eval_numer := {var}") } else { format!( - "quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, r), {var}, r)" + "quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), {var}, R)" ) }; lines.push(line); diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 701cd64..8f2d4f9 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -465,25 +465,25 @@ where ) }, &|(mut acc, var)| { - let (lines, var) = self.init_var(format!("sub(r, {var})"), None); + let (lines, var) = self.init_var(format!("sub(R, {var})"), None); acc.extend(lines); (acc, var) }, &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { - let (lines, var) = self.init_var(format!("addmod({lhs_var}, {rhs_var}, r)"), None); + let (lines, var) = self.init_var(format!("addmod({lhs_var}, {rhs_var}, R)"), None); lhs_acc.extend(rhs_acc); lhs_acc.extend(lines); (lhs_acc, var) }, &|(mut lhs_acc, lhs_var), (rhs_acc, rhs_var)| { - let (lines, var) = self.init_var(format!("mulmod({lhs_var}, {rhs_var}, r)"), None); + let (lines, var) = self.init_var(format!("mulmod({lhs_var}, {rhs_var}, R)"), None); lhs_acc.extend(rhs_acc); lhs_acc.extend(lines); (lhs_acc, var) }, &|(mut acc, var), scalar| { let scalar = u256_string(scalar); - let (lines, var) = self.init_var(format!("mulmod({var}, {scalar}, r)"), None); + let (lines, var) = self.init_var(format!("mulmod({var}, {scalar}, R)"), None); acc.extend(lines); (acc, var) }, @@ -616,8 +616,11 @@ where } } + // TODO: optimiize this by maintaing a cache of previous var stored in static memory and if + // any of the steps require the same var then just return the pointer to the var instead of encoding it again + fn reset(&self) { - *self.static_mem_ptr.borrow_mut() = Default::default(); + *self.static_mem_ptr.borrow_mut() = 0x20; *self.encoded_var_cache.borrow_mut() = Default::default(); } diff --git a/src/test.rs b/src/test.rs index e2518e0..e7ae3b5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -97,9 +97,9 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); - // print verifier_solidity + // // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); - // print vk_solidity + // // print vk_solidity // println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 47f8851..057148f 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; contract Halo2Verifier { uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; + uint256 internal constant R = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 internal constant PROOF_LEN_CPTR = {{ proof_len_cptr }}; uint256 internal constant PROOF_CPTR = {{ proof_cptr }}; uint256 internal constant NUM_INSTANCE_CPTR = {{ proof_cptr + (proof_len / 32) }}; @@ -213,7 +214,7 @@ contract Halo2Verifier { // Modulus let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // BN254 base field - let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field + let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field TODO: rmv this and replace with constant R // Initialize success as true let success := true diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 4555f97..16f34ea 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -6,6 +6,7 @@ contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; uint256 internal constant PROOF_CPTR = 0x84; uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // BN254 scalar field uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; @@ -36,9 +37,9 @@ contract Halo2Verifier { // and store hash mod r as challenge in challenge_mptr, // and push back hash in 0x00 as the first input for next squeeze. // Return updated (challenge_mptr, hash_mptr). - function squeeze_challenge(challenge_mptr, hash_mptr, r) -> ret0, ret1 { + function squeeze_challenge(challenge_mptr, hash_mptr) -> ret0, ret1 { let hash := keccak256(0x00, hash_mptr) - mstore(challenge_mptr, mod(hash, r)) + mstore(challenge_mptr, mod(hash, R)) mstore(0x00, hash) ret0 := add(challenge_mptr, 0x20) ret1 := 0x20 @@ -49,10 +50,10 @@ contract Halo2Verifier { // and store hash mod r as challenge in challenge_mptr, // and push back hash in 0x00 as the first input for next squeeze. // Return updated (challenge_mptr). - function squeeze_challenge_cont(challenge_mptr, r) -> ret { + function squeeze_challenge_cont(challenge_mptr) -> ret { mstore8(0x20, 0x01) let hash := keccak256(0x00, 0x21) - mstore(challenge_mptr, mod(hash, r)) + mstore(challenge_mptr, mod(hash, R)) mstore(0x00, hash) ret := add(challenge_mptr, 0x20) } @@ -153,7 +154,6 @@ contract Halo2Verifier { } // Modulus - let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field // Initialize success as true let success := true @@ -187,7 +187,7 @@ contract Halo2Verifier { {} { let instance := calldataload(instance_cptr) - success := and(success, lt(instance, r)) + success := and(success, lt(instance, R)) mstore(hash_mptr, instance) instance_cptr := add(instance_cptr, 0x20) hash_mptr := add(hash_mptr, 0x20) @@ -203,9 +203,9 @@ contract Halo2Verifier { let challenges_ptr := add(advices_ptr, 0x20) // start of challenges // Iterate over phases using the loaded num_advices and num_challenges - for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 1) } { + for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 0x40) } { // Calculate proof_cptr_end based on num_advices - let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, mul(phase, 0x40))))) // We use 0x40 because each advice is followed by the corresponding challenge + let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, phase)))) // We use 0x40 because each advice is followed by the corresponding challenge // Phase loop for { } lt(proof_cptr, proof_cptr_end) { } { @@ -213,11 +213,11 @@ contract Halo2Verifier { } // Generate challenges - challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) // Continue squeezing challenges based on num_challenges - for { let c := 1 } lt(c, mload(add(challenges_ptr, mul(phase, 0x40)))) { c := add(c, 1) } { // We - challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) + for { let c := 1 } lt(c, mload(add(challenges_ptr, phase))) { c := add(c, 1) } { // We + challenge_mptr := squeeze_challenge_cont(challenge_mptr) } } @@ -228,7 +228,7 @@ contract Halo2Verifier { {} { let eval := calldataload(proof_cptr) - success := and(success, lt(eval, r)) + success := and(success, lt(eval, R)) mstore(hash_mptr, eval) proof_cptr := add(proof_cptr, 0x20) hash_mptr := add(hash_mptr, 0x20) @@ -237,12 +237,12 @@ contract Halo2Verifier { // Read batch opening proof and generate challenges {%- match scheme %} {%- when Bdfg21 %} - challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // zeta - challenge_mptr := squeeze_challenge_cont(challenge_mptr, r) // nu + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) // zeta + challenge_mptr := squeeze_challenge_cont(challenge_mptr) // nu success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) // W - challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr, r) // mu + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) // mu success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) // W' {%- when Gwc19 %} @@ -308,7 +308,7 @@ contract Halo2Verifier { lt(idx, k) { idx := add(idx, 1) } { - x_n := mulmod(x_n, x_n, r) + x_n := mulmod(x_n, x_n, R) } let omega := mload(add(vk_mptr, 0x140)) @@ -324,22 +324,22 @@ contract Halo2Verifier { lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { - mstore(mptr, addmod(x, sub(r, pow_of_omega), r)) - pow_of_omega := mulmod(pow_of_omega, omega, r) + mstore(mptr, addmod(x, sub(R, pow_of_omega),R)) + pow_of_omega := mulmod(pow_of_omega, omega,R) } - let x_n_minus_1 := addmod(x_n, sub(r, 1), r) + let x_n_minus_1 := addmod(x_n, sub(R, 1),R) mstore(mptr_end, x_n_minus_1) - success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20), r) + success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20),R) mptr := x_n_mptr - let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)), r) + let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)),R) for { let pow_of_omega := mload(add(vk_mptr, 0x180)) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { - mstore(mptr, mulmod(l_i_common, mulmod(mload(mptr), pow_of_omega, r), r)) - pow_of_omega := mulmod(pow_of_omega, omega, r) + mstore(mptr, mulmod(l_i_common, mulmod(mload(mptr), pow_of_omega,R),R)) + pow_of_omega := mulmod(pow_of_omega, omega,R) } let l_blind := mload(add(x_n_mptr, 0x20)) @@ -349,7 +349,7 @@ contract Halo2Verifier { lt(l_i_cptr, l_i_cptr_end) { l_i_cptr := add(l_i_cptr, 0x20) } { - l_blind := addmod(l_blind, mload(l_i_cptr), r) + l_blind := addmod(l_blind, mload(l_i_cptr),R) } let instance_eval := 0 @@ -364,7 +364,7 @@ contract Halo2Verifier { l_i_cptr := add(l_i_cptr, 0x20) } { - instance_eval := addmod(instance_eval, mulmod(mload(l_i_cptr), calldataload(instance_cptr), r), r) + instance_eval := addmod(instance_eval, mulmod(mload(l_i_cptr), calldataload(instance_cptr),R),R) } let x_n_minus_1_inv := mload(mptr_end) @@ -394,151 +394,83 @@ contract Halo2Verifier { pop(y) - let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) + let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R) mstore(add(theta_mptr, 0x240), quotient_eval) } - // // Compute quotient evavluation - // // TODO: - // // [X] Gate computations - // // [ ] Permutation computations - // // [ ] Lookup computations - // { - // let quotient_eval_numer - // let y := mload(add(theta_mptr, 0x60)) - // let gate_computations_ptr := add(vk_mptr,(add(vk_mptr, 0x380))) - // let gate_computations_len := mload(gate_computations_ptr) // Remember this length represented in bytes - // let gate_computations := mload(add(gate_computations_ptr, 0x20)) - // let expressions_ptr := add(vk_mptr, EXPRESSIONS_OFFSET) // TODO fill in the correct offset - // let expression := 0x0 // Initialize this to 0. Will set it later in the loop - // let expression_acc := 0 - // let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks - // let constants_ptr := add(vk_mptr, CONSTANTS_OFFSET) // TODO fill in the correct offset - // // Load in the total number of code blocks from the vk constants, right after the number challenges - // for { let code_block := 0 } lt(code_block, gate_computations_len) { code_block := add(code_block, 0x20) } { - // let code_ptr := add(add(gate_computations, code_block), expression_acc) - // // Shift the code_len by the free_static_memory_ptr - // let code_len := add(mload(code_ptr), free_static_memory_ptr) - // // loop through code len - // for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { - // /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word - // expression := mload(add(code_ptr, i)) - // expression_acc := add(expression_acc, 0x20) - // // Load in the least significant byte located of the `expression` word to get the operation type - // let byte := and(expression, 0xFF) - - // // Determine which operation to peform and then store the result in the next available memory slot. - - // // 0x00 => Advice/Fixed expression - // if (eq(byte, 0x00)) { - // // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - // mstore(i,calldataload(and(shr(8, expressions), 0xFFFF))) - // } - // // 0x01 => Negated expression - // else if (eq(byte, 0x01)) { - // // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - // mstore(i,sub(r, mload(and(shr(8, expressions), 0xFFFF)))) - // } - // // 0x02 => Sum expression - // else if (eq(byte, 0x02)) { - // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - // mstore(i,addmod(mload(and(shr(8, expressions), 0xFFFF)),mload(and(shr(24, expressions), 0xFFFF)),r)) - // } - // // 0x03 => Product/scalar expression - // else if (eq(byte, 0x03)) { - // // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - // mstore(i,mulmod(mload(and(shr(8, expressions), 0xFFFF)),mload(and(shr(24, expressions), 0xFFFF)),r)) - // } - // } - // // at the end of each code block we update `quotient_eval_numer` - // if (eq(code_block, 0x00)) { - // // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block - // quotient_eval_numer := mload(code_len) - // } else { - // // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r - // quotient_eval_numer := addmod(quotient_eval_numer, mload(code_len), r) - // } - // } - - // pop(y) - - // let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), r) - // mstore(add(theta_mptr, 0x240), quotient_eval) - // // Check that the quotient evaluation is correct - // success := and(success, eq(quotient_eval, mload(add(theta_mptr, 0x240))) - // } - - // Compute quotient commitment + // Compute quotient evavluation + // TODO: + // [X] Gate computations + // [ ] Permutation computations + // [ ] Lookup computations { - mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) - mstore(0x20, calldataload(add(mload(add(vk_mptr, 0xa0)), 0x20))) - let x_n := mload(add(theta_mptr, 0x180)) - for - { - let cptr := sub(mload(add(vk_mptr, 0xa0)), 0x40) - let cptr_end := sub(mload(add(vk_mptr, 0xc0)), 0x40) + let quotient_eval_numer + let y := mload(add(theta_mptr, 0x60)) + let gate_computations_len_ptr := add(vk_mptr, mload(add(vk_mptr, 0x380))) + let gate_computations_ptr := add(gate_computations_len_ptr, 0x20) + let gate_computations_len := mload(gate_computations_len_ptr) // Remember this length represented in bytes + let gate_computations := mload(gate_computations_ptr) + let expression := 0x0 // Initialize this to 0. Will set it later in the loop. Expression represent the operation type and assocaited operand pointers. + let expression_acc := 0 + let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks + // Load in the total number of code blocks from the vk constants, right after the number challenges + for { let code_block := 0 } lt(code_block, gate_computations_len) { code_block := add(code_block, 0x20) } { + let code_ptr := add(add(gate_computations_ptr, code_block), expression_acc) + // Shift the code_len by the free_static_memory_ptr + let code_len := add(mload(code_ptr), free_static_memory_ptr) + // loop through code len + for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { + /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word + expression := mload(add(code_ptr, i)) + expression_acc := add(expression_acc, 0x20) + + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expression, 0xFF) + // 0x00 => Advice/Fixed expression + case 0x00 { + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(i,calldataload(and(shr(8, expression), 0xFFFF))) + } + // 0x01 => Negated expression + case 0x01 { + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(i,sub(R, mload(and(shr(8, expression), 0xFFFF)))) + } + // 0x02 => Sum expression + case 0x02 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(i,addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } + // 0x03 => Product/scalar expression + case 0x03 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(i,mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } } - lt(cptr_end, cptr) - {} - { - success := ec_mul_acc(success, x_n) - success := ec_add_acc(success, calldataload(cptr), calldataload(add(cptr, 0x20))) - cptr := sub(cptr, 0x40) - } - mstore(add(theta_mptr, 0x260), mload(0x00)) - mstore(add(theta_mptr, 0x280), mload(0x20)) - } - // Compute pairing lhs and rhs - { - {%- for code_block in pcs_computations %} - { - {%- for line in code_block %} - {{ line }} - {%- endfor %} + // at the end of each code block we update `quotient_eval_numer` + + // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block + switch eq(code_block, 0) + case 1 { + quotient_eval_numer := mload(sub(code_len, free_static_memory_ptr)) + } + case 0 { + // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r + quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(sub(code_len, free_static_memory_ptr)), R) + } } - {%- endfor %} - } - // Random linear combine with accumulator - if mload(add(vk_mptr, 0x01a0)) { - mstore(0x00, mload(add(theta_mptr, 0x100))) - mstore(0x20, mload(add(theta_mptr, 0x120))) - mstore(0x40, mload(add(theta_mptr, 0x140))) - mstore(0x60, mload(add(theta_mptr, 0x160))) - mstore(0x80, mload(add(theta_mptr, 0x2c0))) - mstore(0xa0, mload(add(theta_mptr, 0x2e0))) - mstore(0xc0, mload(add(theta_mptr, 0x300))) - mstore(0xe0, mload(add(theta_mptr, 0x320))) - let challenge := mod(keccak256(0x00, 0x100), r) - - // [pairing_lhs] += challenge * [acc_lhs] - success := ec_mul_acc(success, challenge) - success := ec_add_acc(success, mload(add(theta_mptr, 0x2c0)), mload(add(theta_mptr, 0x2e0))) - mstore(add(theta_mptr, 0x2c0), mload(0x00)) - mstore(add(theta_mptr, 0x2e0), mload(0x20)) - - // [pairing_rhs] += challenge * [acc_rhs] - mstore(0x00, mload(add(theta_mptr, 0x140))) - mstore(0x20, mload(add(theta_mptr, 0x160))) - success := ec_mul_acc(success, challenge) - success := ec_add_acc(success, mload(add(theta_mptr, 0x300)), mload(add(theta_mptr, 0x320))) - mstore(add(theta_mptr, 0x300), mload(0x00)) - mstore(add(theta_mptr, 0x320), mload(0x20)) - } + pop(y) - // // Perform pairing - success := ec_pairing( - success, - vk_mptr, - mload(add(theta_mptr, 0x2c0)), - mload(add(theta_mptr, 0x2e0)), - mload(add(theta_mptr, 0x300)), - mload(add(theta_mptr, 0x320)) - ) - + let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R) + // mstore(add(theta_mptr, 0x240), quotient_eval) + // Check that the quotient evaluation is correct + success := and(success, eq(quotient_eval, mload(add(theta_mptr, 0x240)))) + } // Revert if anything fails if iszero(success) { diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 8faf834..26bce37 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -23,18 +23,18 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ num_advices_user_challenges.len()|hex_padded(64) }}) // num_advices_user_challenges length + mstore({{ (32 * offset)|hex_padded(4) }}, {{(2 * 32 * num_advices_user_challenges.len())|hex_padded(64) }}) // num_advices_user_challenges length {%- for (x, y) in num_advices_user_challenges %} {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() + 1 %} mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // num_advices[{{ loop.index0 }}].x mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ gate_computations.len()|hex_padded(64) }}) // gate_computations length + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.len())|hex_padded(64) }}) // gate_computations length {%- for (gate_computation, acc) in gate_computations %} {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} {%- let offset = base_offset + loop.index0 + acc %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ gate_computation.len()|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computation.len())|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] {%- for operation in gate_computation %} {%- let offset = offset + loop.index0 + 1 %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ operation|hex_padded(64) }}) // gate_computation[{{ loop.index0 }}] From 792d609c7f49dfb29d66a631627424241b247bcb Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 12 Jul 2024 22:12:28 -0500 Subject: [PATCH 23/65] *hardcode R as a constant. --- src/codegen.rs | 10 +-- src/codegen/evaluator.rs | 62 +++++++------- src/codegen/pcs.rs | 62 +++++++------- src/evm.rs | 19 ++++- src/test.rs | 4 +- templates/Halo2Verifier.sol | 20 ++--- templates/Halo2VerifierReusable.sol | 122 ++++++++++++++++++++-------- 7 files changed, 186 insertions(+), 113 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 57f7b9f..398340c 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -367,8 +367,6 @@ impl<'a> SolidityGenerator<'a> { }) .collect(); - println!("total length: {:?}", cumulative_length); - let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_expressions.len() * 0x20); @@ -533,13 +531,13 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations: Vec> = chain![ - evaluator.gate_computations(), - // evaluator.permutation_computations(true), - // evaluator.lookup_computations(Some(vk_lookup_const_table), true) + // evaluator.gate_computations(), + evaluator.permutation_computations(true), + evaluator.lookup_computations(Some(vk_lookup_const_table), true) ] .enumerate() .map(|(idx, (mut lines, var))| { - let line = if idx == 0 { + let line = if idx == usize::MAX { format!("quotient_eval_numer := {var}") } else { format!( diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 8f2d4f9..735e5cb 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -65,20 +65,20 @@ where data.permutation_z_evals.first().map(|(z, _, _)| { vec![ format!("let l_0 := mload({l_0})"), - format!("let eval := addmod(l_0, sub(r, mulmod(l_0, {z}, r)), r)"), + format!("let eval := addmod(l_0, sub(R, mulmod(l_0, {z}, R)), R)"), ] }), data.permutation_z_evals.last().map(|(z, _, _)| { - let item = "addmod(mulmod(perm_z_last, perm_z_last, r), sub(r, perm_z_last), r)"; + let item = "addmod(mulmod(perm_z_last, perm_z_last, R), sub(R, perm_z_last), R)"; vec![ format!("let perm_z_last := {z}"), - format!("let eval := mulmod(mload({l_last}), {item}, r)"), + format!("let eval := mulmod(mload({l_last}), {item}, R)"), ] }), data.permutation_z_evals.iter().tuple_windows().map( |((_, _, z_i_last), (z_j, _, _))| { - let item = format!("addmod({z_j}, sub(r, {z_i_last}), r)"); - vec![format!("let eval := mulmod(mload({l_0}), {item}, r)")] + let item = format!("addmod({z_j}, sub(R, {z_i_last}), R)"); + vec![format!("let eval := mulmod(mload({l_0}), {item}, R)")] } ), izip!( @@ -98,28 +98,28 @@ where columns.iter().flat_map(|column| { let perm_eval = &data.permutation_evals[column]; let eval = self.eval(*column.column_type(), column.index(), 0); - let item = format!("mulmod(beta, {perm_eval}, r)"); + let item = format!("mulmod(beta, {perm_eval}, R)"); [format!( - "lhs := mulmod(lhs, addmod(addmod({eval}, {item}, r), gamma, r), r)" + "lhs := mulmod(lhs, addmod(addmod({eval}, {item}, R), gamma, R), R)" )] }), (chunk_idx == 0) - .then(|| format!("mstore(0x00, mulmod(beta, mload({}), r))", x_mptr)), + .then(|| format!("mstore(0x00, mulmod(beta, mload({}), R))", x_mptr)), columns.iter().enumerate().flat_map(|(idx, column)| { let eval = self.eval(*column.column_type(), column.index(), 0); - let item = format!("addmod(addmod({eval}, mload(0x00), r), gamma, r)"); + let item = format!("addmod(addmod({eval}, mload(0x00), R), gamma, R)"); chain![ - [format!("rhs := mulmod(rhs, {item}, r)")], + [format!("rhs := mulmod(rhs, {item}, R)")], (!(chunk_idx == last_chunk_idx && idx == last_column_idx)) - .then(|| "mstore(0x00, mulmod(mload(0x00), DELTA, r))".to_string()), + .then(|| "mstore(0x00, mulmod(mload(0x00), DELTA, R))".to_string()), ] }), { - let item = format!("addmod(mload({l_last}), mload({l_blind}), r)"); - let item = format!("sub(r, mulmod(left_sub_right, {item}, r))"); + let item = format!("addmod(mload({l_last}), mload({l_blind}), R)"); + let item = format!("sub(R, mulmod(left_sub_right, {item}, R))"); [ - format!("let left_sub_right := addmod(lhs, sub(r, rhs), r)"), - format!("let eval := addmod(left_sub_right, {item}, r)"), + format!("let left_sub_right := addmod(lhs, sub(R, rhs), R)"), + format!("let eval := addmod(left_sub_right, {item}, R)"), ] } ] @@ -193,11 +193,11 @@ where [ vec![ format!("let l_0 := mload({l_0})"), - format!("let eval := mulmod(l_0, {phi}, r)"), + format!("let eval := mulmod(l_0, {phi}, R)"), ], vec![ format!("let l_last := mload({l_last})"), - format!("let eval := mulmod(l_last, {phi}, r)"), + format!("let eval := mulmod(l_last, {phi}, R)"), ], chain![ [ @@ -211,9 +211,9 @@ where table_lines, [format!("table := {table_0}")], rest_tables.iter().map(|table| format!( - "table := addmod(mulmod(table, theta, r), {table}, r)" + "table := addmod(mulmod(table, theta, R), {table}, R)" )), - [format!("table := addmod(table, beta, r)")], + [format!("table := addmod(table, beta, R)")], ]), // TODO: break this into it's own function on the solidity side of things, // calling it within a for loop. @@ -256,9 +256,9 @@ where input_lines, [format!("{ident} := {input_0}")], rest_inputs.iter().map(|input| format!( - "{ident} := addmod(mulmod({ident}, theta, r), {input}, r)" + "{ident} := addmod(mulmod({ident}, theta, R), {input}, R)" )), - [format!("{ident} := addmod({ident}, beta, r)")], + [format!("{ident} := addmod({ident}, beta, R)")], ]), ] }), @@ -277,10 +277,10 @@ where code_block::<1, false>(chain![ [format!("let tmp := {ident_0}")], chain![rest_idents] - .map(|ident| format!("tmp := mulmod(tmp, {ident}, r)")), - [format!("rhs := addmod(rhs, tmp, r)"),], + .map(|ident| format!("tmp := mulmod(tmp, {ident}, R)")), + [format!("rhs := addmod(rhs, tmp, R)"),], (i == num_inputs - 1) - .then(|| format!("rhs := mulmod(rhs, table, r)")), + .then(|| format!("rhs := mulmod(rhs, table, R)")), ]) } }), @@ -288,21 +288,21 @@ where code_block::<1, false>(chain![ [format!("let tmp := input_0")], (1..num_inputs) - .map(|idx| format!("tmp := mulmod(tmp, input_{idx}, r)")), + .map(|idx| format!("tmp := mulmod(tmp, input_{idx}, R)")), [ - format!("rhs := addmod(rhs, sub(r, mulmod({m}, tmp, r)), r)"), + format!("rhs := addmod(rhs, sub(R, mulmod({m}, tmp, R)), R)"), { - let item = format!("addmod({phi_next}, sub(r, {phi}), r)"); - format!("lhs := mulmod(mulmod(table, tmp, r), {item}, r)") + let item = format!("addmod({phi_next}, sub(R, {phi}), R)"); + format!("lhs := mulmod(mulmod(table, tmp, R), {item}, R)") }, ], ]), { let l_inactive = - format!("addmod(mload({l_blind}), mload({l_last}), r)"); - let l_active = format!("addmod(1, sub(r, {l_inactive}), r)"); + format!("addmod(mload({l_blind}), mload({l_last}), R)"); + let l_active = format!("addmod(1, sub(R, {l_inactive}), R)"); [format!( - "let eval := mulmod({l_active}, addmod(lhs, sub(r, rhs), r), r)" + "let eval := mulmod({l_active}, addmod(lhs, sub(R, rhs), R), R)" )] }, ] diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 627518a..c038fe5 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -272,7 +272,7 @@ pub(crate) fn bdfg21_computations( format!("let x := mload({})", x).as_str(), format!("let omega := mload({})", omega).as_str(), format!("let omega_inv := mload({})", omega_inv).as_str(), - "let x_pow_of_omega := mulmod(x, omega, r)" + "let x_pow_of_omega := mulmod(x, omega, R)" ] .map(str::to_string), (1..=max_rot).flat_map(|rot| { @@ -281,12 +281,12 @@ pub(crate) fn bdfg21_computations( .get(&rot) .map(|point| format!("mstore({}, x_pow_of_omega)", point.ptr())), (rot != max_rot) - .then(|| { "x_pow_of_omega := mulmod(x_pow_of_omega, omega, r)".to_string() }) + .then(|| { "x_pow_of_omega := mulmod(x_pow_of_omega, omega, R)".to_string() }) ] }), [ format!("mstore({}, x)", points[&0].ptr()), - format!("x_pow_of_omega := mulmod(x, omega_inv, r)") + format!("x_pow_of_omega := mulmod(x, omega_inv, R)") ], (min_rot..0).rev().flat_map(|rot| { chain![ @@ -294,7 +294,7 @@ pub(crate) fn bdfg21_computations( .get(&rot) .map(|point| format!("mstore({}, x_pow_of_omega)", point.ptr())), (rot != min_rot).then(|| { - "x_pow_of_omega := mulmod(x_pow_of_omega, omega_inv, r)".to_string() + "x_pow_of_omega := mulmod(x_pow_of_omega, omega_inv, R)".to_string() }) ] }) @@ -318,7 +318,7 @@ pub(crate) fn bdfg21_computations( "point_mptr := add(point_mptr, 0x20)", ] .map(str::to_string), - ["mstore(mptr, addmod(mu, sub(r, mload(point_mptr)), r))".to_string()], + ["mstore(mptr, addmod(mu, sub(R, mload(point_mptr)), R))".to_string()], ) }, ["let s".to_string()], @@ -328,7 +328,7 @@ pub(crate) fn bdfg21_computations( mu_minus_points[sets[0].rots().first().unwrap()] )], chain![sets[0].rots().iter().skip(1)] - .map(|rot| { format!("s := mulmod(s, {}, r)", mu_minus_points[rot]) }), + .map(|rot| { format!("s := mulmod(s, {}, R)", mu_minus_points[rot]) }), [format!("mstore({}, s)", vanishing_0.ptr())], ], ["let diff".to_string()], @@ -339,7 +339,7 @@ pub(crate) fn bdfg21_computations( .map(|rot| format!("diff := {}", mu_minus_points[rot])) .unwrap_or_else(|| "diff := 1".to_string())], chain![set.diffs().iter().skip(1)] - .map(|rot| { format!("diff := mulmod(diff, {}, r)", mu_minus_points[rot]) }), + .map(|rot| { format!("diff := mulmod(diff, {}, R)", mu_minus_points[rot]) }), [format!("mstore({}, diff)", diff.ptr())], (set_idx == 0).then(|| format!("mstore({}, diff)", diff_0.ptr())), ] @@ -373,15 +373,15 @@ pub(crate) fn bdfg21_computations( [coeff_points .first() .map(|(point_i, point_j)| { - format!("coeff := addmod({point_i}, sub(r, {point_j}), r)") + format!("coeff := addmod({point_i}, sub(R, {point_j}), R)") }) .unwrap_or_else(|| { "coeff := 1".to_string() })], coeff_points.iter().skip(1).map(|(point_i, point_j)| { - let item = format!("addmod({point_i}, sub(r, {point_j}), r)"); - format!("coeff := mulmod(coeff, {item}, r)") + let item = format!("addmod({point_i}, sub(R, {point_j}), R)"); + format!("coeff := mulmod(coeff, {item}, R)") }), [ - format!("coeff := mulmod(coeff, {}, r)", mu_minus_points[rot_i]), + format!("coeff := mulmod(coeff, {}, R)", mu_minus_points[rot_i]), format!("mstore({}, coeff)", coeff.ptr()) ], ] @@ -393,7 +393,7 @@ pub(crate) fn bdfg21_computations( let normalized_coeff_computations = chain![ [ - format!("success := batch_invert(success, 0, {first_batch_invert_end}, r)"), + format!("success := batch_invert(success, 0, {first_batch_invert_end})"), format!("let diff_0_inv := {diff_0}"), format!("mstore({}, diff_0_inv)", diffs[0].ptr()), ], @@ -404,7 +404,7 @@ pub(crate) fn bdfg21_computations( ], "lt(mptr, mptr_end)", ["mptr := add(mptr, 0x20)".to_string()], - ["mstore(mptr, mulmod(mload(mptr), diff_0_inv, r))".to_string()], + ["mstore(mptr, mulmod(mload(mptr), diff_0_inv, R))".to_string()], ), ] .collect_vec(); @@ -445,17 +445,17 @@ pub(crate) fn bdfg21_computations( chain![evals.iter().enumerate()] .flat_map(|(eval_idx, eval)| { let is_first_eval = group_idx == 0 && eval_idx == 0; - let item = format!("mulmod(coeff, {eval}, r)"); + let item = format!("mulmod(coeff, {eval}, R)"); chain![ (!is_first_eval).then(|| format!( - "r_eval := mulmod(r_eval, zeta, r)" + "r_eval := mulmod(r_eval, zeta, R)" )), - [format!("r_eval := addmod(r_eval, {item}, r)")], + [format!("r_eval := addmod(r_eval, {item}, R)")], ] }) .collect_vec() } else { - let item = "mulmod(coeff, calldataload(mptr), r)"; + let item = "mulmod(coeff, calldataload(mptr), R)"; for_loop( [ format!("let mptr := {}", evals[0].ptr()), @@ -464,7 +464,7 @@ pub(crate) fn bdfg21_computations( "lt(mptr_end, mptr)".to_string(), ["mptr := sub(mptr, 0x20)".to_string()], [format!( - "r_eval := addmod(mulmod(r_eval, zeta, r), {item}, r)" + "r_eval := addmod(mulmod(r_eval, zeta, R), {item}, R)" )], ) } @@ -475,15 +475,15 @@ pub(crate) fn bdfg21_computations( .flat_map(|(idx, evals)| { chain![ izip!(evals, coeffs).map(|(eval, coeff)| { - let item = format!("mulmod({coeff}, {eval}, r)"); - format!("r_eval := addmod(r_eval, {item}, r)") + let item = format!("mulmod({coeff}, {eval}, R)"); + format!("r_eval := addmod(r_eval, {item}, R)") }), - (idx != 0).then(|| format!("r_eval := mulmod(r_eval, zeta, r)")), + (idx != 0).then(|| format!("r_eval := mulmod(r_eval, zeta, R)")), ] }) .collect_vec() }, - (set_idx != 0).then(|| format!("r_eval := mulmod(r_eval, {set_coeff}, r)")), + (set_idx != 0).then(|| format!("r_eval := mulmod(r_eval, {set_coeff}, R)")), [format!("mstore({}, r_eval)", r_eval.ptr())], ] .collect_vec() @@ -496,7 +496,7 @@ pub(crate) fn bdfg21_computations( [format!("let sum := {coeff_0}")], rest_coeffs .iter() - .map(|coeff_mptr| format!("sum := addmod(sum, {coeff_mptr}, r)")), + .map(|coeff_mptr| format!("sum := addmod(sum, {coeff_mptr}, R)")), [format!("mstore({}, sum)", sum.ptr())], ] .collect_vec() @@ -521,9 +521,9 @@ pub(crate) fn bdfg21_computations( ["mstore(mptr, mload(sum_mptr))".to_string()], ), [ - format!("success := batch_invert(success, 0, {second_batch_invert_end}, r)"), + format!("success := batch_invert(success, 0, {second_batch_invert_end})"), format!( - "let r_eval := mulmod(mload({}), {}, r)", + "let r_eval := mulmod(mload({}), {}, R)", second_batch_invert_end - 1, r_evals.last().unwrap() ) @@ -541,8 +541,8 @@ pub(crate) fn bdfg21_computations( ] .map(str::to_string), [ - format!("r_eval := mulmod(r_eval, mload({nu}), r)").as_str(), - "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), r), r)" + format!("r_eval := mulmod(r_eval, mload({nu}), R)").as_str(), + "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), R), R)" ] .map(str::to_string), ), @@ -625,13 +625,13 @@ pub(crate) fn bdfg21_computations( }), (!is_first_set) .then(|| { - let scalar = format!("mulmod(nu, {set_coeff}, r)"); + let scalar = format!("mulmod(nu, {set_coeff}, R)"); chain![ [ format!("success := ec_mul_tmp(success, {scalar})"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), ], - (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), r)")) + (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), R)")) ] }) .into_iter() @@ -642,11 +642,11 @@ pub(crate) fn bdfg21_computations( [ format!("mstore(0x80, mload({}))", g1_x), format!("mstore(0xa0, mload({}))", g1_y), - format!("success := ec_mul_tmp(success, sub(r, mload({r_eval})))"), + format!("success := ec_mul_tmp(success, sub(R, mload({r_eval})))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w.x()), format!("mstore(0xa0, {})", w.y()), - format!("success := ec_mul_tmp(success, sub(r, {vanishing_0}))"), + format!("success := ec_mul_tmp(success, sub(R, {vanishing_0}))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w_prime.x()), format!("mstore(0xa0, {})", w_prime.y()), diff --git a/src/evm.rs b/src/evm.rs index 1156ee3..851a5a2 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -50,7 +50,9 @@ pub fn encode_calldata( pub(crate) mod test { pub use revm; use revm::{ - primitives::{Address, CreateScheme, ExecutionResult, Output, TransactTo, TxEnv}, + primitives::{ + Address, CfgEnv, CreateScheme, Env, ExecutionResult, Output, TransactTo, TxEnv, + }, InMemoryDB, EVM, }; use std::{ @@ -145,6 +147,21 @@ pub(crate) mod test { .len() } + /// Return a version of the evm that allows for unlimited deployments sizes. + pub fn unlimited() -> Self { + let mut cfg: CfgEnv = Default::default(); + cfg.limit_contract_code_size = Some(usize::MAX); + Self { + evm: EVM { + env: Env { + cfg, + ..Default::default() + }, + db: Some(Default::default()), + }, + } + } + /// Apply create transaction with given `bytecode` as creation bytecode. /// Return created `address`. /// diff --git a/src/test.rs b/src/test.rs index e7ae3b5..5326f0c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -57,7 +57,7 @@ fn run_render>() { let verifier_creation_code = compile_solidity(verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); - let mut evm = Evm::default(); + let mut evm = Evm::unlimited(); let verifier_address = evm.create(verifier_creation_code); let verifier_runtime_code_size = evm.code_size(verifier_address); @@ -80,7 +80,7 @@ fn run_render_separately>() { let verifier_creation_code = compile_solidity(&verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); - let mut evm = Evm::default(); + let mut evm = Evm::unlimited(); let verifier_address = evm.create(verifier_creation_code); let verifier_runtime_code_size = evm.code_size(verifier_address); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 057148f..279a551 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -119,7 +119,7 @@ contract Halo2Verifier { // Batch invert values in memory[mptr_start..mptr_end] in place. // Return updated (success). - function batch_invert(success, mptr_start, mptr_end, r) -> ret { + function batch_invert(success, mptr_start, mptr_end) -> ret { let gp_mptr := mptr_end let gp := mload(mptr_start) let mptr := add(mptr_start, 0x20) @@ -128,19 +128,19 @@ contract Halo2Verifier { lt(mptr, sub(mptr_end, 0x20)) {} { - gp := mulmod(gp, mload(mptr), r) + gp := mulmod(gp, mload(mptr), R) mstore(gp_mptr, gp) mptr := add(mptr, 0x20) gp_mptr := add(gp_mptr, 0x20) } - gp := mulmod(gp, mload(mptr), r) + gp := mulmod(gp, mload(mptr), R) mstore(gp_mptr, 0x20) mstore(add(gp_mptr, 0x20), 0x20) mstore(add(gp_mptr, 0x40), 0x20) mstore(add(gp_mptr, 0x60), gp) - mstore(add(gp_mptr, 0x80), sub(r, 2)) - mstore(add(gp_mptr, 0xa0), r) + mstore(add(gp_mptr, 0x80), sub(R, 2)) + mstore(add(gp_mptr, 0xa0), R) ret := and(success, staticcall(gas(), 0x05, gp_mptr, 0xc0, gp_mptr, 0x20)) let all_inv := mload(gp_mptr) @@ -152,14 +152,14 @@ contract Halo2Verifier { lt(second_mptr, mptr) {} { - let inv := mulmod(all_inv, mload(gp_mptr), r) - all_inv := mulmod(all_inv, mload(mptr), r) + let inv := mulmod(all_inv, mload(gp_mptr), R) + all_inv := mulmod(all_inv, mload(mptr), R) mstore(mptr, inv) mptr := sub(mptr, 0x20) gp_mptr := sub(gp_mptr, 0x20) } - let inv_first := mulmod(all_inv, mload(second_mptr), r) - let inv_second := mulmod(all_inv, mload(first_mptr), r) + let inv_first := mulmod(all_inv, mload(second_mptr), R) + let inv_second := mulmod(all_inv, mload(first_mptr), R) mstore(first_mptr, inv_first) mstore(second_mptr, inv_second) } @@ -389,7 +389,7 @@ contract Halo2Verifier { } let x_n_minus_1 := addmod(x_n, sub(r, 1), r) mstore(mptr_end, x_n_minus_1) - success := batch_invert(success, X_N_MPTR, add(mptr_end, 0x20), r) + success := batch_invert(success, X_N_MPTR, add(mptr_end, 0x20)) mptr := X_N_MPTR let l_i_common := mulmod(x_n_minus_1, mload(N_INV_MPTR), r) diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 16f34ea..158c87a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -60,7 +60,7 @@ contract Halo2Verifier { // Batch invert values in memory[mptr_start..mptr_end] in place. // Return updated (success). - function batch_invert(success, mptr_start, mptr_end, r) -> ret { + function batch_invert(success, mptr_start, mptr_end) -> ret { let gp_mptr := mptr_end let gp := mload(mptr_start) let mptr := add(mptr_start, 0x20) @@ -69,19 +69,19 @@ contract Halo2Verifier { lt(mptr, sub(mptr_end, 0x20)) {} { - gp := mulmod(gp, mload(mptr), r) + gp := mulmod(gp, mload(mptr), R) mstore(gp_mptr, gp) mptr := add(mptr, 0x20) gp_mptr := add(gp_mptr, 0x20) } - gp := mulmod(gp, mload(mptr), r) + gp := mulmod(gp, mload(mptr), R) mstore(gp_mptr, 0x20) mstore(add(gp_mptr, 0x20), 0x20) mstore(add(gp_mptr, 0x40), 0x20) mstore(add(gp_mptr, 0x60), gp) - mstore(add(gp_mptr, 0x80), sub(r, 2)) - mstore(add(gp_mptr, 0xa0), r) + mstore(add(gp_mptr, 0x80), sub(R, 2)) + mstore(add(gp_mptr, 0xa0), R) ret := and(success, staticcall(gas(), 0x05, gp_mptr, 0xc0, gp_mptr, 0x20)) let all_inv := mload(gp_mptr) @@ -93,14 +93,14 @@ contract Halo2Verifier { lt(second_mptr, mptr) {} { - let inv := mulmod(all_inv, mload(gp_mptr), r) - all_inv := mulmod(all_inv, mload(mptr), r) + let inv := mulmod(all_inv, mload(gp_mptr), R) + all_inv := mulmod(all_inv, mload(mptr), R) mstore(mptr, inv) mptr := sub(mptr, 0x20) gp_mptr := sub(gp_mptr, 0x20) } - let inv_first := mulmod(all_inv, mload(second_mptr), r) - let inv_second := mulmod(all_inv, mload(first_mptr), r) + let inv_first := mulmod(all_inv, mload(second_mptr), R) + let inv_second := mulmod(all_inv, mload(first_mptr), R) mstore(first_mptr, inv_first) mstore(second_mptr, inv_second) } @@ -329,7 +329,7 @@ contract Halo2Verifier { } let x_n_minus_1 := addmod(x_n, sub(R, 1),R) mstore(mptr_end, x_n_minus_1) - success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20),R) + success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20)) mptr := x_n_mptr let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)),R) @@ -379,32 +379,15 @@ contract Halo2Verifier { mstore(add(theta_mptr, 0x220), instance_eval) } - // Compute quotient evavluation - { - let quotient_eval_numer - let y := mload(add(theta_mptr, 0x60)) - - {%- for code_block in quotient_eval_numer_computations %} - { - {%- for line in code_block %} - {{ line }} - {%- endfor %} - } - {%- endfor %} - - pop(y) - - let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R) - mstore(add(theta_mptr, 0x240), quotient_eval) - } // Compute quotient evavluation // TODO: // [X] Gate computations // [ ] Permutation computations // [ ] Lookup computations + let quotient_eval_numer { - let quotient_eval_numer + // let quotient_eval_numer let y := mload(add(theta_mptr, 0x60)) let gate_computations_len_ptr := add(vk_mptr, mload(add(vk_mptr, 0x380))) let gate_computations_ptr := add(gate_computations_len_ptr, 0x20) @@ -464,14 +447,89 @@ contract Halo2Verifier { } } + {%- for code_block in quotient_eval_numer_computations %} + { + {%- for line in code_block %} + {{ line }} + {%- endfor %} + } + {%- endfor %} + pop(y) let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R) - // mstore(add(theta_mptr, 0x240), quotient_eval) - // Check that the quotient evaluation is correct - success := and(success, eq(quotient_eval, mload(add(theta_mptr, 0x240)))) + mstore(add(theta_mptr, 0x240), quotient_eval) + } + + // Compute quotient commitment + { + mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) + mstore(0x20, calldataload(add(mload(add(vk_mptr, 0xa0)), 0x20))) + let x_n := mload(add(theta_mptr, 0x180)) + for + { + let cptr := sub(mload(add(vk_mptr, 0xa0)), 0x40) + let cptr_end := sub(mload(add(vk_mptr, 0xc0)), 0x40) + } + lt(cptr_end, cptr) + {} + { + success := ec_mul_acc(success, x_n) + success := ec_add_acc(success, calldataload(cptr), calldataload(add(cptr, 0x20))) + cptr := sub(cptr, 0x40) + } + mstore(add(theta_mptr, 0x260), mload(0x00)) + mstore(add(theta_mptr, 0x280), mload(0x20)) } + // Compute pairing lhs and rhs + { + {%- for code_block in pcs_computations %} + { + {%- for line in code_block %} + {{ line }} + {%- endfor %} + } + {%- endfor %} + } + + // Random linear combine with accumulator + if mload(add(vk_mptr, 0x01a0)) { + mstore(0x00, mload(add(theta_mptr, 0x100))) + mstore(0x20, mload(add(theta_mptr, 0x120))) + mstore(0x40, mload(add(theta_mptr, 0x140))) + mstore(0x60, mload(add(theta_mptr, 0x160))) + mstore(0x80, mload(add(theta_mptr, 0x2c0))) + mstore(0xa0, mload(add(theta_mptr, 0x2e0))) + mstore(0xc0, mload(add(theta_mptr, 0x300))) + mstore(0xe0, mload(add(theta_mptr, 0x320))) + let challenge := mod(keccak256(0x00, 0x100), R) + + // [pairing_lhs] += challenge * [acc_lhs] + success := ec_mul_acc(success, challenge) + success := ec_add_acc(success, mload(add(theta_mptr, 0x2c0)), mload(add(theta_mptr, 0x2e0))) + mstore(add(theta_mptr, 0x2c0), mload(0x00)) + mstore(add(theta_mptr, 0x2e0), mload(0x20)) + + // [pairing_rhs] += challenge * [acc_rhs] + mstore(0x00, mload(add(theta_mptr, 0x140))) + mstore(0x20, mload(add(theta_mptr, 0x160))) + success := ec_mul_acc(success, challenge) + success := ec_add_acc(success, mload(add(theta_mptr, 0x300)), mload(add(theta_mptr, 0x320))) + mstore(add(theta_mptr, 0x300), mload(0x00)) + mstore(add(theta_mptr, 0x320), mload(0x20)) + } + + // Perform pairing + success := ec_pairing( + success, + vk_mptr, + mload(add(theta_mptr, 0x2c0)), + mload(add(theta_mptr, 0x2e0)), + mload(add(theta_mptr, 0x300)), + mload(add(theta_mptr, 0x320)) + ) + // Revert if anything fails if iszero(success) { revert(0x00, 0x00) From d3894926e5356ff9bb8f895986db63ce241a5432 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 19 Jul 2024 18:13:57 -0500 Subject: [PATCH 24/65] *encode permutation columns. --- src/codegen.rs | 45 +++--- src/codegen/evaluator.rs | 84 +++++++++- src/codegen/template.rs | 4 + src/codegen/util.rs | 5 +- templates/Halo2VerifierReusable.sol | 242 +++++++++++++++++++++------- templates/Halo2VerifyingKey.sol | 24 ++- 6 files changed, 310 insertions(+), 94 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 398340c..a479a4f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -9,6 +9,7 @@ use crate::codegen::{ expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, }, }; +use evaluator::PermutationDataEncoded; use halo2_proofs::{ halo2curves::{bn256, ff::Field}, plonk::VerifyingKey, @@ -234,6 +235,7 @@ impl<'a> SolidityGenerator<'a> { U256::from(self.meta.challenge_indices.len() * 32), ), ("gate_computations_len_offset", U256::from(0)), // dummy gate_computations_len_offset + ("permutation_computations_len_offset", U256::from(0)), // dummy permutation_computations_len_offset ] } else { vec![ @@ -278,6 +280,7 @@ impl<'a> SolidityGenerator<'a> { num_advices_user_challenges: vec![], gate_computations: vec![], gate_computations_total_length: 0, + permutation_computations: PermutationDataEncoded::default(), }; if !separate { @@ -319,23 +322,17 @@ impl<'a> SolidityGenerator<'a> { let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); // set instance_cptr it at position 7 of the constants. - constants[7] = ("instance_cptr", instance_cptr); + constants[7].1 = instance_cptr; let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; // set first_quotient_x_cptr at position 6 of the constants. - constants[6] = ( - "first_quotient_x_cptr", - U256::from(first_quotient_x_cptr.value().as_usize()), - ); + constants[6].1 = U256::from(first_quotient_x_cptr.value().as_usize()); let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); // set last_quotient_x_cptr at position 5 of the constants. - constants[5] = ( - "last_quotient_x_cptr", - U256::from(last_quotient_x_cptr.value().as_usize()), - ); + constants[5].1 = U256::from(last_quotient_x_cptr.value().as_usize()); let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); @@ -354,11 +351,11 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); - // Fill in the gate computations with dummy values. + // Fill in the gate computations with dummy values. (maintains the correct shape) let mut cumulative_length = 0; let dummy_gate_computations: Vec<(Vec, usize)> = chain![evaluator_dummy.gate_computations()] - .map(|(lines, _)| { + .map(|lines| { let operations = lines.iter().map(|_line| U256::from(0)).collect::>(); let length = operations.len(); let gate_computation = (operations, cumulative_length); @@ -381,10 +378,13 @@ impl<'a> SolidityGenerator<'a> { + ((num_advices_user_challenges.len() * 0x40) + 0x20); // set the gate_computations_len_offset at position 28. - constants[28] = ( - "gate_computations_len_offset", - U256::from(gate_computations_len_offset), - ); + constants[28].1 = U256::from(gate_computations_len_offset); + + let permutations_computations_len_offset = gate_computations_len_offset + + (0x20 * (dummy_gate_computations.len() + cumulative_length) + 0x20); + + // set the gate_computations_len_offset at position 28. + constants[29].1 = U256::from(permutations_computations_len_offset); let mut vk = Halo2VerifyingKey { constants, @@ -394,11 +394,12 @@ impl<'a> SolidityGenerator<'a> { num_advices_user_challenges, gate_computations: dummy_gate_computations, gate_computations_total_length: cumulative_length, + permutation_computations: evaluator_dummy.permutation_computations(), }; // Now generate the real vk_mptr with a vk that has the correct length let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84)); - // Generte the real data. + // Generate the real data. let data = Data::new( &self.meta, &vk, @@ -407,10 +408,10 @@ impl<'a> SolidityGenerator<'a> { true, ); // replace the mock vk_mptr with the real vk_mptr - vk.constants[1] = ("vk_mptr", U256::from(vk_mptr)); + vk.constants[1].1 = U256::from(vk_mptr); // replace the mock vk_len with the real vk_len let vk_len = vk.len(); - vk.constants[2] = ("vk_len", U256::from(vk_len)); + vk.constants[2].1 = U256::from(vk_len); // Regenerate the gate computations with the correct offsets. let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); @@ -433,7 +434,7 @@ impl<'a> SolidityGenerator<'a> { let mut cumulative_length = 0; let gate_computations: Vec<(Vec, usize)> = chain![evaluator.gate_computations()] - .map(|(lines, _)| { + .map(|lines| { let operations = lines .iter() .map(|line: &ruint::Uint<256, 4>| U256::from(*line)) @@ -445,8 +446,8 @@ impl<'a> SolidityGenerator<'a> { }) .collect(); - vk.gate_computations = gate_computations; // NOTE: We don't need to replace the gate_computations_total_length since we are only potentially modifying the offsets for each constant mload operation. + vk.gate_computations = gate_computations; vk } @@ -464,7 +465,7 @@ impl<'a> SolidityGenerator<'a> { let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), evaluator.permutation_computations(false), - evaluator.lookup_computations(None, false) + evaluator.lookup_computations(None, false), ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -532,7 +533,7 @@ impl<'a> SolidityGenerator<'a> { let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations: Vec> = chain![ // evaluator.gate_computations(), - evaluator.permutation_computations(true), + // evaluator.permutation_computations(true), evaluator.lookup_computations(Some(vk_lookup_const_table), true) ] .enumerate() diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 735e5cb..d32fa58 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -13,7 +13,7 @@ use regex::Regex; use ruint::aliases::U256; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; -use super::util::{get_memory_ptr, Ptr}; +use super::util::{get_memory_ptr, Ptr, Word}; #[derive(Debug)] pub(crate) struct Evaluator<'a, F: PrimeField> { @@ -536,6 +536,39 @@ pub enum OperandMem { StaticMemory, } +// Holds the encoded data stored in the separate VK +// needed to perform the permutation computations of +// the quotient evaluation portion of the reusable verifier. +#[derive(Clone, PartialEq, Eq)] +pub struct PermutationDataEncoded { + pub(crate) z_evals_last_idx: U256, + pub(crate) chunk_offset: U256, + pub(crate) permutation_z_evals: Vec, + pub(crate) column_evals: Vec>, +} + +impl Default for PermutationDataEncoded { + fn default() -> Self { + PermutationDataEncoded { + z_evals_last_idx: U256::from(0), + chunk_offset: U256::from(0), + permutation_z_evals: Vec::new(), + column_evals: Vec::new(), + } + } +} + +impl PermutationDataEncoded { + pub fn len(&self) -> usize { + if self == &Self::default() { + 0 + } else { + 3 + self.permutation_z_evals.len() + + self.column_evals.iter().map(Vec::len).sum::() + } + } +} + impl<'a, F> EvaluatorVK<'a, F> where F: PrimeField, @@ -556,7 +589,7 @@ where } } - pub fn gate_computations(&self) -> Vec<(Vec, U256)> { + pub fn gate_computations(&self) -> Vec> { self.cs .gates() .iter() @@ -565,9 +598,37 @@ where .collect() } - // pub fn permutation_computations(&self, _separate: bool) -> Vec<(Vec, String)> { - // todo!() - // } + pub fn permutation_computations(&self) -> PermutationDataEncoded { + let Self { meta, data, .. } = self; + let permutation_z_evals_last_idx = 32 * (data.permutation_z_evals.len() - 1); + let chunk_offset = meta.permutation_chunk_len + 1; + let permutation_z_evals: Vec = data + .permutation_z_evals + .iter() + .map(|z| self.encode_z_evaluation_word(z)) + .collect(); + let column_evals: Vec> = meta + .permutation_columns + .chunks(meta.permutation_chunk_len) + .map(|columns| { + columns + .iter() + .map(|column| { + let perm_eval = + U256::from(data.permutation_evals[column].ptr().value().as_usize()); + let eval = self.eval_encoded(*column.column_type(), column.index(), 0); + eval | (perm_eval << 24) + }) + .collect() + }) + .collect(); + PermutationDataEncoded { + z_evals_last_idx: U256::from(permutation_z_evals_last_idx), + chunk_offset: U256::from(chunk_offset), + permutation_z_evals, + column_evals, + } + } // #[cfg(feature = "mv-lookup")] // pub fn lookup_computations( @@ -612,7 +673,7 @@ where .as_usize(), ), ), - Any::Instance => unimplemented!(), + Any::Instance => self.encode_single_operand(1_u8, U256::from(0)), // On the EVM side the 0x0 op heere will inidicate that we need to perform the l_0 mload operation. } } @@ -632,10 +693,17 @@ where U256::from(op) | (ptr << 8) } - fn evaluate_and_reset(&self, expression: &Expression) -> (Vec, U256) { + fn encode_z_evaluation_word(&self, value: &(Word, Word, Word)) -> U256 { + let (z_i, z_j, z_i_last) = value; + U256::from(z_i.ptr().value().as_usize()) + | U256::from(z_j.ptr().value().as_usize() << 16) + | U256::from(z_i_last.ptr().value().as_usize() << 32) + } + + fn evaluate_and_reset(&self, expression: &Expression) -> Vec { let result = self.evaluate_encode(expression); self.reset(); - result + result.0 } fn evaluate_encode(&self, expression: &Expression) -> (Vec, U256) { diff --git a/src/codegen/template.rs b/src/codegen/template.rs index bcc3d4d..332c351 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -6,6 +6,8 @@ use askama::{Error, Template}; use ruint::aliases::U256; use std::fmt; +use super::evaluator::PermutationDataEncoded; + #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] pub(crate) struct Halo2VerifyingKey { @@ -16,6 +18,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) const_expressions: Vec, pub(crate) gate_computations: Vec<(Vec, usize)>, pub(crate) gate_computations_total_length: usize, + pub(crate) permutation_computations: PermutationDataEncoded, } impl Halo2VerifyingKey { @@ -28,6 +31,7 @@ impl Halo2VerifyingKey { + (self.gate_computations.len() * 0x20 + 0x20) // Sum up the lengths of al the nested vectors + (self.gate_computations_total_length * 0x20) + + (self.permutation_computations.len() * 0x20) } } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 3a401c2..6612f2b 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -403,7 +403,8 @@ impl Data { + vk.const_expressions.len() + (2 * vk.num_advices_user_challenges.len() + 1) + (vk.gate_computations.len() + 1) - + (vk.gate_computations_total_length); + + (vk.gate_computations_total_length) + + (vk.permutation_computations.len()); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; @@ -613,7 +614,7 @@ impl Data { Word::range(permutation_eval_cptr) ) .collect(); - let permutation_z_evals = Word::range(permutation_z_eval_cptr) + let permutation_z_evals: Vec<(Word, Word, Word)> = Word::range(permutation_z_eval_cptr) .take(3 * meta.num_permutation_zs) .tuples() .collect_vec(); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 158c87a..5c4cc99 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -153,6 +153,83 @@ contract Halo2Verifier { ret := and(ret, mload(0x00)) } + // Returns start of computaions ptr and length of SoA layout memory + // encoding for quotient evaluation data (gate, permutation and lookup computations) + function soa_layout_metadata(offset, vk_mptr) -> ret0, ret1 { + let computations_len_ptr := add(vk_mptr, mload(add(vk_mptr, offset))) + ret0 := add(computations_len_ptr, 0x20) + ret1 := mload(computations_len_ptr) // Remember this length represented in bytes + } + + + function perm_comp_layout_metadata(offset, vk_mptr) -> ret0, ret1, ret2, ret3 { + let computations_ptr, computations_len := soa_layout_metadata(offset, vk_mptr) + let permutation_z_evals_ptr := add(computations_ptr, 0x20) + let permutation_chunk := mload(computations_ptr) // Don't multiply by 0x20 word size here. Just encode permutation_chunk_len + 1 + let permutation_z_evals := mload(permutation_z_evals_ptr) + ret0 := computations_len + ret1 := permutation_z_evals_ptr + ret2 := permutation_chunk + ret3 := permutation_z_evals + } + + function col_evaluations(z, chunk, permutation_z_evals_ptr, theta_mptr) { + let gamma := mload(add(theta_mptr, 0x40)) + let beta := mload(add(theta_mptr, 0x20)) + let x := mload(add(theta_mptr, 0x80)) + let l_last := mload(add(theta_mptr, 0x1c0)) + let l_blind := mload(add(theta_mptr, 0x1e0)) + let i_eval := mload(add(theta_mptr, 0x220)) + // Extract the index 1 and index 0 z evaluations from the z word. + let lhs := calldataload(and(shr(16,z), 0xFFFF)) + let rhs := calldataload(and(z, 0xFFFF)) + // loop through the word_len_chunk + for { let j := 0x20 } lt(j, chunk) { j := add(j, 0x20) } { + let col_word := mload(add(permutation_z_evals_ptr, j)) + let eval := i_eval + if eq(and(col_word, 0xFF), 0x00) { + eval := calldataload(and(shr(8, col_word), 0xFFFF)) + } + lhs := mulmod(lhs, addmod(addmod(eval, mulmod(beta, calldataload(and(shr(24, col_word), 0xFFFF)), R), R), gamma, R), R) + rhs := mulmod(rhs, addmod(addmod(eval, mload(0x00), R), gamma, R), R) + mstore(0x00, mulmod(mload(0x00), DELTA, R)) + } + let left_sub_right := addmod(lhs, sub(R, rhs), R) + let fsm_ptr := mload(0x20) + mstore(fsm_ptr, addmod(left_sub_right, sub(R, mulmod(left_sub_right, addmod(l_last, l_blind, R), R)), R)) + mstore(0x20, add(fsm_ptr,0x20)) + } + + function z_evals(z, permutation_chunk, perm_z_last_ptr, permutation_z_evals_ptr, theta_mptr, l_0, y, quotient_eval_numer) -> ret { + // Initialize the free static memory pointer to store the column evals. + mstore(0x20, 0x40) + // Iterate through the tuple window length ( permutation_z_evals_len.len() - 1 ) offset by one word. + for { } lt(permutation_z_evals_ptr, perm_z_last_ptr) { } { + let next_z_ptr := add(permutation_z_evals_ptr, permutation_chunk) + let z_j := mload(next_z_ptr) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(l_0, addmod(calldataload(and(z_j, 0xFFFF)), sub(R, calldataload(and(shr(32,z), 0xFFFF))), R), R), + R + ) + col_evaluations(z, permutation_chunk, permutation_z_evals_ptr, theta_mptr) + permutation_z_evals_ptr := next_z_ptr + z := z_j + } + // Due to the fact that permutation_columns.len() in H2 might not be divisble by permutation_chunk_len, the last column length might be less than permutation_chunk_len + // We store this length right after the last perm_z_evals word. + let chunk_offset_last_ptr := add(permutation_z_evals_ptr, 0x20) + permutation_chunk := mload(chunk_offset_last_ptr) // Remeber to store (columns.len() + 1) * 32 here + col_evaluations(z, permutation_chunk, chunk_offset_last_ptr, theta_mptr) + // iterate through col_evaluations to update the quotient_eval_numer accumulator + let end_ptr := mload(0x20) + for { let j := 0x40 } lt(j, end_ptr) { j := add(j, 0x20) } { + quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(j), R) + } + ret := quotient_eval_numer + } + + // Modulus // Initialize success as true @@ -383,82 +460,125 @@ contract Halo2Verifier { // Compute quotient evavluation // TODO: // [X] Gate computations - // [ ] Permutation computations + // [X] Permutation computations // [ ] Lookup computations - let quotient_eval_numer { - // let quotient_eval_numer + let quotient_eval_numer let y := mload(add(theta_mptr, 0x60)) - let gate_computations_len_ptr := add(vk_mptr, mload(add(vk_mptr, 0x380))) - let gate_computations_ptr := add(gate_computations_len_ptr, 0x20) - let gate_computations_len := mload(gate_computations_len_ptr) // Remember this length represented in bytes - let gate_computations := mload(gate_computations_ptr) - let expression := 0x0 // Initialize this to 0. Will set it later in the loop. Expression represent the operation type and assocaited operand pointers. - let expression_acc := 0 - let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks - // Load in the total number of code blocks from the vk constants, right after the number challenges - for { let code_block := 0 } lt(code_block, gate_computations_len) { code_block := add(code_block, 0x20) } { - let code_ptr := add(add(gate_computations_ptr, code_block), expression_acc) - // Shift the code_len by the free_static_memory_ptr - let code_len := add(mload(code_ptr), free_static_memory_ptr) - // loop through code len - for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { - /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word - expression := mload(add(code_ptr, i)) - expression_acc := add(expression_acc, 0x20) - - // Load in the least significant byte of the `expression` word to get the operation type - // Then determine which operation to peform and then store the result in the next available memory slot. - switch and(expression, 0xFF) - // 0x00 => Advice/Fixed expression - case 0x00 { - // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - mstore(i,calldataload(and(shr(8, expression), 0xFFFF))) - } - // 0x01 => Negated expression - case 0x01 { - // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - mstore(i,sub(R, mload(and(shr(8, expression), 0xFFFF)))) + { + // Gate computations / expression evaluations. + let computations_ptr, computations_len := soa_layout_metadata(0x380, vk_mptr) + let expression := 0x0 // Initialize this to 0. Will set it later in the loop. Expression represent the operation type and assocaited operand pointers. + let expression_acc := 0 + let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks + // Load in the total number of code blocks from the vk constants, right after the number challenges + for { let code_block := 0 } lt(code_block, computations_len) { code_block := add(code_block, 0x20) } { + let code_ptr := add(add(computations_ptr, code_block), expression_acc) + // Shift the code_len by the free_static_memory_ptr + let code_len := add(mload(code_ptr), free_static_memory_ptr) + // loop through code len + for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { + /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word + expression := mload(add(code_ptr, i)) + expression_acc := add(expression_acc, 0x20) + + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expression, 0xFF) + // 0x00 => Advice/Fixed expression + case 0x00 { + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(i,calldataload(and(shr(8, expression), 0xFFFF))) + } + // 0x01 => Negated expression + case 0x01 { + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(i,sub(R, mload(and(shr(8, expression), 0xFFFF)))) + } + // 0x02 => Sum expression + case 0x02 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(i,addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } + // 0x03 => Product/scalar expression + case 0x03 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(i,mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } } - // 0x02 => Sum expression - case 0x02 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(i,addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + + // at the end of each code block we update `quotient_eval_numer` + + // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block + switch eq(code_block, 0) + case 1 { + quotient_eval_numer := mload(sub(code_len, free_static_memory_ptr)) } - // 0x03 => Product/scalar expression - case 0x03 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(i,mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + case 0 { + // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r + quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(sub(code_len, free_static_memory_ptr)), R) } } - - // at the end of each code block we update `quotient_eval_numer` - - // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block - switch eq(code_block, 0) - case 1 { - quotient_eval_numer := mload(sub(code_len, free_static_memory_ptr)) + } + { + // Permutation computations + let computations_len, permutation_z_evals_ptr, permutation_chunk, permutation_z_evals := perm_comp_layout_metadata(0x3a0, vk_mptr) + let l_0 := mload(add(theta_mptr, 0x200)) + { + // Get the first and second LSG bytes from the first permutation_z_evals word to load in (z, _, _) + let eval := addmod(l_0, sub(R, mulmod(l_0, calldataload(and(permutation_z_evals, 0xFFFF)), R)), R) + quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), eval, R) } - case 0 { - // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r - quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(sub(code_len, free_static_memory_ptr)), R) + + { + // Load in the last permutation_z_evals word + let perm_z_last_ptr := add(mul(computations_len, permutation_chunk), permutation_z_evals_ptr) + let perm_z_last := calldataload(and(mload(perm_z_last_ptr), 0xFFFF)) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + mload(add(theta_mptr, 0x1C0)), + addmod( + mulmod(perm_z_last, perm_z_last, R), + sub(R, perm_z_last), + R + ), + R + ), + R + ) + + mstore(0x00, mulmod(mload(add(theta_mptr, 0x20)), mload(add(theta_mptr, 0x80)), R)) + + quotient_eval_numer := z_evals( + permutation_z_evals, + // Update the chunk offset to be in bytes + mul(0x20, permutation_chunk), + perm_z_last_ptr, + permutation_z_evals_ptr, + theta_mptr, + l_0, + y, + quotient_eval_numer + ) } } - - {%- for code_block in quotient_eval_numer_computations %} { - {%- for line in code_block %} - {{ line }} + // Lookup computations + {%- for code_block in quotient_eval_numer_computations %} + { + {%- for line in code_block %} + {{ line }} + {%- endfor %} + } {%- endfor %} } - {%- endfor %} - pop(y) - let quotient_eval := mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R) - mstore(add(theta_mptr, 0x240), quotient_eval) + mstore(add(theta_mptr, 0x240), mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R)) + } // Compute quotient commitment diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 26bce37..bc6da22 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -40,7 +40,29 @@ contract Halo2VerifyingKey { mstore({{ (32 * offset)|hex_padded(4) }}, {{ operation|hex_padded(64) }}) // gate_computation[{{ loop.index0 }}] {%- endfor %} {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length ))|hex() }}) + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.z_evals_last_idx|hex_padded(64) }}) // z_evals_last_idx + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 3 + gate_computations.len() + gate_computations_total_length %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.chunk_offset|hex_padded(64) }}) // chunk_offset + {%- for z_eval in permutation_computations.permutation_z_evals %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 4 + gate_computations.len() + gate_computations_total_length %} + {%- let offset = base_offset + loop.index0 * (permutation_computations.column_evals[0].len() + 1)%} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_eval[{{ loop.index0 }}] + {%- let last_index = permutation_computations.permutation_z_evals.len() - 1 %} + {%- let plus_one %} + {%- if loop.index0 == last_index %} + {%- let offset = offset + 1 %} + {%- let plus_one = 1 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * (permutation_computations.column_evals[last_index].len() + 1))|hex_padded(64) }}) // chunk_offset_last + {%- else -%} + {%- let plus_one = 0 -%} + {%- endif %} + {%- for column_eval in permutation_computations.column_evals[loop.index0] %} + {%- let offset = offset + loop.index0 + 1 + plus_one %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ column_eval|hex_padded(64) }}) // column_eval[{{ loop.index0 }}] + {%- endfor %} + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() ))|hex() }}) } } } From 5c5f76deaa7a70a0dce44954228fb745ebb2b437 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 23 Jul 2024 14:55:37 -0500 Subject: [PATCH 25/65] *bump solc version to 0.8.26 *encode lookup computations in VK. --- .github/workflows/ci.yml | 2 +- src/codegen.rs | 35 ++-- src/codegen/evaluator.rs | 283 +++++++++++++++++++++++++--- src/codegen/template.rs | 5 +- src/codegen/util.rs | 3 +- src/test.rs | 4 +- templates/Halo2Verifier.sol | 2 +- templates/Halo2VerifierReusable.sol | 212 ++++++++++++++++----- templates/Halo2VerifyingKey.sol | 23 ++- 9 files changed, 465 insertions(+), 104 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f96586..0ce9041 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: cache-on-failure: true - name: Install solc - run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.21 && solc --version + run: cargo install svm-rs && svm install 0.8.26 && svm use 0.8.26 && solc --version - name: Run test run: cargo test --workspace --all-features --all-targets -- --nocapture diff --git a/src/codegen.rs b/src/codegen.rs index a479a4f..dbb5f58 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -9,7 +9,7 @@ use crate::codegen::{ expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, }, }; -use evaluator::PermutationDataEncoded; +use evaluator::{LookupsDataEncoded, PermutationDataEncoded}; use halo2_proofs::{ halo2curves::{bn256, ff::Field}, plonk::VerifyingKey, @@ -236,6 +236,7 @@ impl<'a> SolidityGenerator<'a> { ), ("gate_computations_len_offset", U256::from(0)), // dummy gate_computations_len_offset ("permutation_computations_len_offset", U256::from(0)), // dummy permutation_computations_len_offset + ("lookup_computations_len_offset", U256::from(0)), // dummy lookup_computations_len_offset ] } else { vec![ @@ -281,6 +282,7 @@ impl<'a> SolidityGenerator<'a> { gate_computations: vec![], gate_computations_total_length: 0, permutation_computations: PermutationDataEncoded::default(), + lookup_computations: LookupsDataEncoded::default(), }; if !separate { @@ -386,6 +388,12 @@ impl<'a> SolidityGenerator<'a> { // set the gate_computations_len_offset at position 28. constants[29].1 = U256::from(permutations_computations_len_offset); + let lookup_computations_len_offset = permutations_computations_len_offset + + (0x20 * evaluator_dummy.permutation_computations().len()); + + // set the lookup_computations_len_offset at position 30. + constants[30].1 = U256::from(lookup_computations_len_offset); + let mut vk = Halo2VerifyingKey { constants, fixed_comms, @@ -395,6 +403,7 @@ impl<'a> SolidityGenerator<'a> { gate_computations: dummy_gate_computations, gate_computations_total_length: cumulative_length, permutation_computations: evaluator_dummy.permutation_computations(), + lookup_computations: evaluator_dummy.lookup_computations(0), }; // Now generate the real vk_mptr with a vk that has the correct length let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84)); @@ -448,6 +457,9 @@ impl<'a> SolidityGenerator<'a> { // NOTE: We don't need to replace the gate_computations_total_length since we are only potentially modifying the offsets for each constant mload operation. vk.gate_computations = gate_computations; + // We need to replace the lookup_computations so that the constant mptrs in the encoded input expessions have the correct offsets. + vk.lookup_computations = + evaluator.lookup_computations(vk_mptr + lookup_computations_len_offset); vk } @@ -529,26 +541,6 @@ impl<'a> SolidityGenerator<'a> { }); let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); - - let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); - let quotient_eval_numer_computations: Vec> = chain![ - // evaluator.gate_computations(), - // evaluator.permutation_computations(true), - evaluator.lookup_computations(Some(vk_lookup_const_table), true) - ] - .enumerate() - .map(|(idx, (mut lines, var))| { - let line = if idx == usize::MAX { - format!("quotient_eval_numer := {var}") - } else { - format!( - "quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), {var}, R)" - ) - }; - lines.push(line); - lines - }) - .collect(); // iterate through the quotient_eval_numer_computations and determine longest Vec within the Vec>. // TODO: Use this to estimate static working memory size // let quotient_eval_numer_computations_longest = quotient_eval_numer_computations @@ -570,7 +562,6 @@ impl<'a> SolidityGenerator<'a> { scheme: self.scheme, num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, num_evals: self.meta.num_evals, - quotient_eval_numer_computations, pcs_computations, } } diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index d32fa58..56ed0a4 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -569,6 +569,72 @@ impl PermutationDataEncoded { } } +#[derive(Clone, PartialEq, Eq)] +pub struct InputsEncoded { + pub(crate) expression: Vec, + pub(crate) vars: U256, + pub(crate) acc: usize, +} + +// Holds the encoded data stored in the separate VK +// needed to perform the lookup computations for one lookup table +// in the quotient evaluation portion of the reusable verifier. +#[derive(Clone, PartialEq, Eq)] +pub struct LookupEncoded { + pub(crate) evals: U256, + pub(crate) table_lines: U256, + pub(crate) acc: usize, + pub(crate) inputs: Vec, +} + +// For each element of the lookups vector we have a word for: +// 1) the evals (cptr, cptr, cptr), +// 2) table lines Vec packed into a single (throws an error if table_lines.len() > 16) +// 3) outer_inputs_len (inputs.0.len()) +// For each element of the inputs vector in LookupEncoded we have a word for: +// 1) input_lines_len (inputs[i].0.len()) packed into a single (throws an error if inputs.1.len() > 16) +// 2) inputs (inputs[i].1) packed into a single (throws an error if inputs.1.len() > 16) +// Then we have a word for each step in the expression evaluation. This is the +// sum of the lengths of the inputs. +impl LookupEncoded { + pub fn len(&self) -> usize { + 3 + (2 * self.inputs.len()) + + self + .inputs + .iter() + .map(|inputs| inputs.expression.len()) + .sum::() + } +} + +// Holds the encoded data stored in the separate VK +// needed to perform the lookup computations of all the lookup tables +// needed in the quotient evaluation portion of the reusable verifier. +#[derive(Clone, PartialEq, Eq)] +pub struct LookupsDataEncoded { + pub(crate) end_ptr: U256, + pub(crate) lookups: Vec, +} + +impl Default for LookupsDataEncoded { + fn default() -> Self { + LookupsDataEncoded { + end_ptr: U256::from(0), + lookups: Vec::new(), + } + } +} + +impl LookupsDataEncoded { + pub fn len(&self) -> usize { + if self == &Self::default() { + 0 + } else { + 1 + self.lookups.iter().map(LookupEncoded::len).sum::() + } + } +} + impl<'a, F> EvaluatorVK<'a, F> where F: PrimeField, @@ -583,7 +649,7 @@ where cs, meta, data, - static_mem_ptr: RefCell::new(0x20), + static_mem_ptr: RefCell::new(0x00), encoded_var_cache: Default::default(), const_cache: RefCell::new(const_cache), } @@ -605,7 +671,7 @@ where let permutation_z_evals: Vec = data .permutation_z_evals .iter() - .map(|z| self.encode_z_evaluation_word(z)) + .map(|z| self.encode_triplet_evaluation_word(z)) .collect(); let column_evals: Vec> = meta .permutation_columns @@ -630,23 +696,134 @@ where } } - // #[cfg(feature = "mv-lookup")] - // pub fn lookup_computations( - // &self, - // _vk_lookup_const_table: Option, super::util::Ptr>>, - // _separate: bool, - // ) -> Vec<(Vec, String)> { - // todo!() - // } - - // #[cfg(not(feature = "mv-lookup"))] - // pub fn lookup_computations( - // &self, - // _vk_lookup_const_table: Option, super::util::Ptr>>, - // _separate: bool, - // ) -> Vec<(Vec, String)> { - // todo!() - // } + #[cfg(feature = "mv-lookup")] + pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { + let evaluate_table = |expressions: &Vec<_>| { + // println!("expressions: {:?}", expressions); + let (lines, inputs) = expressions + .iter() + .map(|expression| self.evaluate_encode_calldata(expression)) + .fold((Vec::new(), Vec::new()), |mut acc, result| { + acc.0.extend(result.0); + acc.1.push(result.1); + acc + }); + // assert that lines.len() <= and inputs.len() == inputs.len() + assert!(lines.len() <= 16); + assert!(inputs.len() == lines.len()); + self.reset(); + (lines, inputs) + }; + + let evaluate_inputs = |idx: usize, expressions: &Vec<_>| { + // println!("expressions: {:?}", expressions); + let offset = 0xa0; // offset to store theta offset ptrs used + // in the lookup computations. + let fsm = (0x20 * idx) + offset; + self.set_static_mem_ptr(fsm); + let (lines, inputs) = expressions + .iter() + .map(|expression| self.evaluate_encode(expression)) + .fold((Vec::new(), Vec::new()), |mut acc, result| { + acc.0.extend(result.0); + acc.1.push(result.1); + acc + }); + // TODO: Add this free memory pointer + // incrementation to the estimate free static memory function. + self.reset(); + (lines, inputs) + }; + + let inputs_tables = self + .cs + .lookups() + .iter() + .map(|lookup| { + let inputs_iter = lookup.input_expressions().iter().enumerate(); + let inputs = inputs_iter + .clone() + .map(|(idx, expressions)| { + let (lines, inputs) = evaluate_inputs(idx, expressions); + (lines, inputs) + }) + .collect_vec(); + let table = evaluate_table(lookup.table_expressions()); + (inputs, table) + }) + .collect_vec(); + + let mut accumulator = 0; + + let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) + .map(|(inputs_tables, evals)| { + let (inputs, (table_lines, _)) = inputs_tables.clone(); + let evals = self.encode_triplet_evaluation_word(evals); + let table_lines = self.encode_pack_ptrs(&table_lines); + let mut inner_accumulator = 0; + let inputs: Vec = inputs + .iter() + .map(|(input_lines, inputs)| { + let inputs = self.encode_pack_ptrs(inputs); + let res = InputsEncoded { + expression: input_lines.clone(), + vars: inputs, + acc: inner_accumulator, + }; + inner_accumulator += input_lines.len() + 1; + res + }) + .collect_vec(); + let lookup_encoded = LookupEncoded { + evals, + table_lines, + inputs: inputs.clone(), + acc: accumulator, + }; + accumulator += inputs + .iter() + .map(|inputs| inputs.expression.len()) + .sum::() + + (inputs.len() * 2); + lookup_encoded + }) + .collect_vec(); + let mut data = LookupsDataEncoded { + lookups, + end_ptr: U256::from(0), + }; + data.end_ptr = U256::from((data.len() * 32) + offset); + data + } + + #[cfg(not(feature = "mv-lookup"))] + pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { + // TODO implement non mv lookup version of this + LookupsDataEncoded::default() + } + + fn eval_encode_raw_cptr( + &self, + column_type: impl Into, + column_index: usize, + rotation: i32, + ) -> U256 { + match column_type.into() { + Any::Advice(_) => U256::from( + self.data.advice_evals[&(column_index, rotation)] + .ptr() + .value() + .as_usize(), + ), + Any::Fixed => U256::from( + self.data.fixed_evals[&(column_index, rotation)] + .ptr() + .value() + .as_usize(), + ), + Any::Instance => unimplemented!(), // On the EVM side the 0x0 op here we will inidicate that we need to perform the l_0 mload operation. + } + } fn eval_encoded( &self, @@ -673,7 +850,7 @@ where .as_usize(), ), ), - Any::Instance => self.encode_single_operand(1_u8, U256::from(0)), // On the EVM side the 0x0 op heere will inidicate that we need to perform the l_0 mload operation. + Any::Instance => self.encode_single_operand(1_u8, U256::from(0)), // On the EVM side the 0x0 op here we will inidicate that we need to perform the l_0 mload operation. } } @@ -681,7 +858,7 @@ where // any of the steps require the same var then just return the pointer to the var instead of encoding it again fn reset(&self) { - *self.static_mem_ptr.borrow_mut() = 0x20; + *self.static_mem_ptr.borrow_mut() = 0x0; *self.encoded_var_cache.borrow_mut() = Default::default(); } @@ -693,19 +870,56 @@ where U256::from(op) | (ptr << 8) } - fn encode_z_evaluation_word(&self, value: &(Word, Word, Word)) -> U256 { + fn encode_triplet_evaluation_word(&self, value: &(Word, Word, Word)) -> U256 { let (z_i, z_j, z_i_last) = value; U256::from(z_i.ptr().value().as_usize()) | U256::from(z_j.ptr().value().as_usize() << 16) | U256::from(z_i_last.ptr().value().as_usize() << 32) } + // pack as many as 16 ptrs into a single word + // throws an error if the number of ptrs is greater than 16 + fn encode_pack_ptrs(&self, ptrs: &[U256]) -> U256 { + let mut packed = U256::from(0); + for (i, ptr) in ptrs.iter().enumerate() { + packed |= *ptr << (i * 16); + } + packed + } + fn evaluate_and_reset(&self, expression: &Expression) -> Vec { + *self.static_mem_ptr.borrow_mut() = 0x0; let result = self.evaluate_encode(expression); self.reset(); result.0 } + fn set_static_mem_ptr(&self, value: usize) { + *self.static_mem_ptr.borrow_mut() = value; + } + + fn evaluate_encode_calldata(&self, expression: &Expression) -> (Vec, U256) { + evaluate_calldata( + expression, + &|query| { + self.init_encoded_var( + self.eval_encode_raw_cptr(Fixed, query.column_index(), query.rotation().0), + OperandMem::Calldata, + ) + }, + &|query| { + self.init_encoded_var( + self.eval_encode_raw_cptr( + Advice::default(), + query.column_index(), + query.rotation().0, + ), + OperandMem::Calldata, + ) + }, + ) + } + fn evaluate_encode(&self, expression: &Expression) -> (Vec, U256) { evaluate( expression, @@ -851,7 +1065,7 @@ fn evaluate( where F: PrimeField, { - let evaluate = |expr| { + let evaluate = |expr: &Expression| { evaluate( expr, constant, fixed, advice, instance, challenge, negated, sum, product, scaled, ) @@ -869,3 +1083,26 @@ where Expression::Scaled(value, scalar) => scaled(evaluate(value), fe_to_u256(*scalar)), } } + +#[allow(clippy::too_many_arguments)] +fn evaluate_calldata( + expression: &Expression, + fixed: &impl Fn(FixedQuery) -> T, + advice: &impl Fn(AdviceQuery) -> T, +) -> T +where + F: PrimeField, +{ + match expression { + Expression::Constant(_) => unreachable!(), + Expression::Selector(_) => unreachable!(), + Expression::Fixed(query) => fixed(*query), + Expression::Advice(query) => advice(*query), + Expression::Instance(_) => unreachable!(), + Expression::Challenge(_) => unreachable!(), + Expression::Negated(_) => unreachable!(), + Expression::Sum(_, _) => unreachable!(), + Expression::Product(_, _) => unreachable!(), + Expression::Scaled(_, _) => unreachable!(), + } +} diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 332c351..ea34204 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -6,7 +6,7 @@ use askama::{Error, Template}; use ruint::aliases::U256; use std::fmt; -use super::evaluator::PermutationDataEncoded; +use super::evaluator::{LookupsDataEncoded, PermutationDataEncoded}; #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] @@ -19,6 +19,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) gate_computations: Vec<(Vec, usize)>, pub(crate) gate_computations_total_length: usize, pub(crate) permutation_computations: PermutationDataEncoded, + pub(crate) lookup_computations: LookupsDataEncoded, } impl Halo2VerifyingKey { @@ -32,6 +33,7 @@ impl Halo2VerifyingKey { // Sum up the lengths of al the nested vectors + (self.gate_computations_total_length * 0x20) + (self.permutation_computations.len() * 0x20) + + (self.lookup_computations.len() * 0x20) } } @@ -62,7 +64,6 @@ pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, pub(crate) num_neg_lagranges: usize, pub(crate) num_evals: usize, - pub(crate) quotient_eval_numer_computations: Vec>, pub(crate) pcs_computations: Vec>, } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 6612f2b..6ae4d6b 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -404,7 +404,8 @@ impl Data { + (2 * vk.num_advices_user_challenges.len() + 1) + (vk.gate_computations.len() + 1) + (vk.gate_computations_total_length) - + (vk.permutation_computations.len()); + + (vk.permutation_computations.len()) + + (vk.lookup_computations.len()); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; diff --git a/src/test.rs b/src/test.rs index 5326f0c..2b66bb7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -97,9 +97,9 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); - // // print verifier_solidity + // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); - // // print vk_solidity + // print vk_solidity // println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 279a551..8a42ef5 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.26; contract Halo2Verifier { uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 5c4cc99..145b222 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.26; contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; @@ -173,7 +173,7 @@ contract Halo2Verifier { ret3 := permutation_z_evals } - function col_evaluations(z, chunk, permutation_z_evals_ptr, theta_mptr) { + function col_evals(z, chunk, permutation_z_evals_ptr, theta_mptr) { let gamma := mload(add(theta_mptr, 0x40)) let beta := mload(add(theta_mptr, 0x20)) let x := mload(add(theta_mptr, 0x80)) @@ -212,7 +212,7 @@ contract Halo2Verifier { mulmod(l_0, addmod(calldataload(and(z_j, 0xFFFF)), sub(R, calldataload(and(shr(32,z), 0xFFFF))), R), R), R ) - col_evaluations(z, permutation_chunk, permutation_z_evals_ptr, theta_mptr) + col_evals(z, permutation_chunk, permutation_z_evals_ptr, theta_mptr) permutation_z_evals_ptr := next_z_ptr z := z_j } @@ -220,8 +220,8 @@ contract Halo2Verifier { // We store this length right after the last perm_z_evals word. let chunk_offset_last_ptr := add(permutation_z_evals_ptr, 0x20) permutation_chunk := mload(chunk_offset_last_ptr) // Remeber to store (columns.len() + 1) * 32 here - col_evaluations(z, permutation_chunk, chunk_offset_last_ptr, theta_mptr) - // iterate through col_evaluations to update the quotient_eval_numer accumulator + col_evals(z, permutation_chunk, chunk_offset_last_ptr, theta_mptr) + // iterate through col_evals to update the quotient_eval_numer accumulator let end_ptr := mload(0x20) for { let j := 0x40 } lt(j, end_ptr) { j := add(j, 0x20) } { quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(j), R) @@ -229,6 +229,37 @@ contract Halo2Verifier { ret := quotient_eval_numer } + function expression_evals(fsmp, code_len, code_ptr) { + for { let i := 0 } lt(i, code_len) { i := add(i, 0x20) } { + /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word + let expression := mload(add(code_ptr, i)) + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expression, 0xFF) + // 0x00 => Advice/Fixed expression + case 0x00 { + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(add(fsmp, i), calldataload(and(shr(8, expression), 0xFFFF))) + } + // 0x01 => Negated expression + case 0x01 { + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(add(fsmp, i), sub(R, mload(and(shr(8, expression), 0xFFFF)))) + } + // 0x02 => Sum expression + case 0x02 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(add(fsmp, i), addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } + // 0x03 => Product/scalar expression + case 0x03 { + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(add(fsmp, i), mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + } + } + } // Modulus @@ -461,7 +492,7 @@ contract Halo2Verifier { // TODO: // [X] Gate computations // [X] Permutation computations - // [ ] Lookup computations + // [X] Lookup computations { let quotient_eval_numer let y := mload(add(theta_mptr, 0x60)) @@ -469,56 +500,29 @@ contract Halo2Verifier { // Gate computations / expression evaluations. let computations_ptr, computations_len := soa_layout_metadata(0x380, vk_mptr) let expression := 0x0 // Initialize this to 0. Will set it later in the loop. Expression represent the operation type and assocaited operand pointers. - let expression_acc := 0 - let free_static_memory_ptr := 0x20 // Initialize at 0x20 b/c 0x00 to store vars that need to persist across certain code blocks + let expression_acc := 0x0 // Load in the total number of code blocks from the vk constants, right after the number challenges for { let code_block := 0 } lt(code_block, computations_len) { code_block := add(code_block, 0x20) } { let code_ptr := add(add(computations_ptr, code_block), expression_acc) // Shift the code_len by the free_static_memory_ptr - let code_len := add(mload(code_ptr), free_static_memory_ptr) - // loop through code len - for { let i := free_static_memory_ptr } lt(i, code_len) { i := add(i, 0x20) } { - /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word - expression := mload(add(code_ptr, i)) - expression_acc := add(expression_acc, 0x20) - - // Load in the least significant byte of the `expression` word to get the operation type - // Then determine which operation to peform and then store the result in the next available memory slot. - switch and(expression, 0xFF) - // 0x00 => Advice/Fixed expression - case 0x00 { - // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - mstore(i,calldataload(and(shr(8, expression), 0xFFFF))) - } - // 0x01 => Negated expression - case 0x01 { - // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - mstore(i,sub(R, mload(and(shr(8, expression), 0xFFFF)))) - } - // 0x02 => Sum expression - case 0x02 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(i,addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) - } - // 0x03 => Product/scalar expression - case 0x03 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(i,mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) - } - } + let code_len := mload(code_ptr) + // call expression_evals to evaluate the expressions in the code block + expression_evals(0x00, code_len, add(code_ptr, 0x20)) + + expression_acc := add(expression_acc, code_len) + + let last_idx := sub(code_len, 0x20) // at the end of each code block we update `quotient_eval_numer` // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block switch eq(code_block, 0) case 1 { - quotient_eval_numer := mload(sub(code_len, free_static_memory_ptr)) + quotient_eval_numer := mload(last_idx) } case 0 { // Otherwise we add the last var in the code block to `quotient_eval_numer` mod r - quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(sub(code_len, free_static_memory_ptr)), R) + quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), mload(last_idx), R) } } } @@ -567,14 +571,124 @@ contract Halo2Verifier { } { // Lookup computations - {%- for code_block in quotient_eval_numer_computations %} - { - {%- for line in code_block %} - {{ line }} - {%- endfor %} + mstore(0x0, mload(add(theta_mptr, 0x1C0))) // l_last + mstore(0x20, mload(add(theta_mptr, 0x200))) // l_0 + mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind + mstore(0x60, mload(theta_mptr)) // theta + mstore(0x80, mload(add(theta_mptr, 0x20))) // beta + let evals_ptr, end_ptr := soa_layout_metadata(0x3c0, vk_mptr) // TODO: Compute the end ptr of the lookup computations space + // iterate through the input_tables_len + for { } lt(evals_ptr, end_ptr) { } { + let evals := mload(evals_ptr) + let phi := and(evals, 0xFFFF) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x20),calldataload(phi), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x00), calldataload(phi), R), + R + ) + let table + // load in the table_lines_len from the evals_ptr + evals_ptr := add(evals_ptr, 0x20) + let table_lines := mload(evals_ptr) + table := calldataload(and(table_lines, 0xFFFF)) + table_lines := shr(16, table_lines) + for { } table_lines { } { + // extract the calldata ptr from the tables_lines + table := addmod( + mulmod(table, mload(0x60), R), + calldataload(and(table_lines, 0xFFFF)), + R + ) + table_lines := shr(16, table_lines) + } + table := addmod(table, mload(0x80), R) + evals_ptr := add(evals_ptr, 0x20) + let outer_inputs_len := mload(evals_ptr) + for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { + evals_ptr := add(evals_ptr, 0x20) + let input_lines_len := mload(evals_ptr) + // call the expression_evals function to evaluate the input_lines + expression_evals(j, input_lines_len, add(evals_ptr, 0x20)) + evals_ptr := add(add(evals_ptr, input_lines_len), 0x20) + let inputs := mload(evals_ptr) + let ident := mload(and(inputs, 0xFFFF)) + inputs := shr(16, inputs) + for { } inputs { } { + // extract the mload ptr from the inputs stored in memory + ident := addmod( + mulmod(ident, mload(0x60), R), + mload(and(inputs, 0xFFFF)), + R + ) + inputs := shr(16, inputs) + } + ident := addmod(ident, mload(0x80), R) + // store ident in free static memory + mstore(j, ident) + } + evals_ptr := add(evals_ptr, 0x20) + let lhs + let rhs + switch eq(outer_inputs_len, 0x20) + case 1 { + rhs := table + } default { + // iterate through the outer_inputs_len + let last_idx := sub(outer_inputs_len, 0x20) + for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { + // iterate through the outer_inputs_len + let tmp := mload(0xa0) + if eq(i, 0){ + tmp := mload(0xc0) + } + for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + if eq(i, j) { + continue + } + tmp := mulmod(tmp, mload(j), R) + + } + rhs := addmod(rhs, tmp, R) + if eq(i, last_idx) { + rhs := mulmod(rhs, table, R) + } + } + } + let tmp := mload(0xa0) + for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + tmp := mulmod(tmp, mload(j), R) + } + rhs := addmod( + rhs, + sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), + R + ) + lhs := mulmod( + mulmod(table, tmp, R), + addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + addmod( + 1, + sub(R, addmod(mload(0x40), mload(0x00), R)), + R + ), + addmod(lhs, sub(R, rhs), R), + R + ), + R + ) } - {%- endfor %} } + pop(y) mstore(add(theta_mptr, 0x240), mulmod(quotient_eval_numer, mload(add(theta_mptr, 0x1a0)), R)) diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index bc6da22..3082b60 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.26; contract Halo2VerifyingKey { constructor() { @@ -47,7 +47,7 @@ contract Halo2VerifyingKey { {%- for z_eval in permutation_computations.permutation_z_evals %} {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 4 + gate_computations.len() + gate_computations_total_length %} {%- let offset = base_offset + loop.index0 * (permutation_computations.column_evals[0].len() + 1)%} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_eval[{{ loop.index0 }}] + mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_evals[{{ loop.index0 }}] {%- let last_index = permutation_computations.permutation_z_evals.len() - 1 %} {%- let plus_one %} {%- if loop.index0 == last_index %} @@ -62,7 +62,24 @@ contract Halo2VerifyingKey { mstore({{ (32 * offset)|hex_padded(4) }}, {{ column_eval|hex_padded(64) }}) // column_eval[{{ loop.index0 }}] {%- endfor %} {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() ))|hex() }}) + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations + {%- for lookup in lookup_computations.lookups %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() + 1 %} + {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] + mstore({{ (32 * (offset + 1))|hex_padded(4) }}, {{ lookup.table_lines|hex_padded(64) }}) // lookup_table_lines[{{ loop.index0 }}] + mstore({{ (32 * (offset + 2))|hex_padded(4) }}, {{ (32 * lookup.inputs.len())|hex_padded(64) }}) // outer_inputs_len[{{ loop.index0 }}] + {%- for input in lookup.inputs %} + {%- let offset = offset + loop.index0 + input.acc + 3 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * input.expression.len())|hex_padded(64) }}) // inputs_len [{{ loop.index0 }}] + {%- for expression in input.expression %} + mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] + {%- endfor %} + mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] + {%- endfor %} + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() + lookup_computations.len() ))|hex() }}) } } } From 21a24444eda99585d3f4838e8e2a34bf74b19df8 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 23 Jul 2024 23:26:22 -0500 Subject: [PATCH 26/65] * refactor generate_vk * offset mapping in reusable verifier * nightly toolchain --- .github/workflows/ci.yml | 5 +- rust-toolchain | 3 +- src/codegen.rs | 360 ++++++++++++++++------------ src/codegen/template.rs | 4 +- templates/Halo2VerifierReusable.sol | 65 ++--- 5 files changed, 252 insertions(+), 185 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ce9041..40ed1c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,9 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: - toolchain: 1.77.2 - profile: minimal + toolchain: nightly-2024-02-06 + override: true + components: rustfmt, clippy - uses: Swatinem/rust-cache@v1 with: diff --git a/rust-toolchain b/rust-toolchain index 6f14058..7f9dbf2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,2 +1,3 @@ [toolchain] -channel = "1.77.2" +channel = "nightly-2024-02-06" +components = ["rustfmt", "clippy"] \ No newline at end of file diff --git a/src/codegen.rs b/src/codegen.rs index dbb5f58..484fa78 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -170,100 +170,140 @@ impl<'a> SolidityGenerator<'a> { Ok((verifier_output, vk_output)) } + fn dummy_vk_constants(separate: bool) -> Vec<(&'static str, U256)> { + if separate { + vec![ + ("vk_digest", U256::from(0)), + ("vk_mptr", U256::from(0)), + ("vk_len", U256::from(0)), + ("num_instances", U256::from(0)), + ("num_advices_user_challenges_offset", U256::from(0)), + ("last_quotient_x_cptr", U256::from(0)), + ("first_quotient_x_cptr", U256::from(0)), + ("instance_cptr", U256::from(0)), + ("k", U256::from(0)), + ("n_inv", U256::from(0)), + ("omega", U256::from(0)), + ("omega_inv", U256::from(0)), + ("omega_inv_to_l", U256::from(0)), + ("has_accumulator", U256::from(0)), + ("acc_offset", U256::from(0)), + ("num_acc_limbs", U256::from(0)), + ("num_acc_limb_bits", U256::from(0)), + ("g1_x", U256::from(0)), + ("g1_y", U256::from(0)), + ("g2_x_1", U256::from(0)), + ("g2_x_2", U256::from(0)), + ("g2_y_1", U256::from(0)), + ("g2_y_2", U256::from(0)), + ("neg_s_g2_x_1", U256::from(0)), + ("neg_s_g2_x_2", U256::from(0)), + ("neg_s_g2_y_1", U256::from(0)), + ("neg_s_g2_y_2", U256::from(0)), + ("challenges_offset", U256::from(0)), + ("gate_computations_len_offset", U256::from(0)), + ("permutation_computations_len_offset", U256::from(0)), + ("lookup_computations_len_offset", U256::from(0)), + ("num_evals", U256::from(0)), + ("num_neg_lagranges", U256::from(0)), + ] + } else { + vec![ + ("vk_digest", U256::from(0)), + ("num_instances", U256::from(0)), + ("k", U256::from(0)), + ("n_inv", U256::from(0)), + ("omega", U256::from(0)), + ("omega_inv", U256::from(0)), + ("omega_inv_to_l", U256::from(0)), + ("has_accumulator", U256::from(0)), + ("acc_offset", U256::from(0)), + ("num_acc_limbs", U256::from(0)), + ("num_acc_limb_bits", U256::from(0)), + ("g1_x", U256::from(0)), + ("g1_y", U256::from(0)), + ("g2_x_1", U256::from(0)), + ("g2_x_2", U256::from(0)), + ("g2_y_1", U256::from(0)), + ("g2_y_2", U256::from(0)), + ("neg_s_g2_x_1", U256::from(0)), + ("neg_s_g2_x_2", U256::from(0)), + ("neg_s_g2_y_1", U256::from(0)), + ("neg_s_g2_y_2", U256::from(0)), + ] + } + } + fn generate_vk(&self, separate: bool) -> Halo2VerifyingKey { - let mut constants = { - let domain = self.vk.get_domain(); - let vk_digest = fr_to_u256(vk_transcript_repr(self.vk)); - let num_instances = U256::from(self.num_instances); - let k = U256::from(domain.k()); - let n_inv = fr_to_u256(bn256::Fr::from(1 << domain.k()).invert().unwrap()); - let omega = fr_to_u256(domain.get_omega()); - let omega_inv = fr_to_u256(domain.get_omega_inv()); - let omega_inv_to_l = { - let l = self.meta.rotation_last.unsigned_abs() as u64; - fr_to_u256(domain.get_omega_inv().pow_vartime([l])) - }; - let has_accumulator = U256::from(self.acc_encoding.is_some() as usize); - let acc_offset = self - .acc_encoding - .map(|acc_encoding| U256::from(acc_encoding.offset)) - .unwrap_or_default(); - let num_acc_limbs = self - .acc_encoding - .map(|acc_encoding| U256::from(acc_encoding.num_limbs)) - .unwrap_or_default(); - let num_acc_limb_bits = self - .acc_encoding - .map(|acc_encoding| U256::from(acc_encoding.num_limb_bits)) - .unwrap_or_default(); - let g1 = self.params.get_g()[0]; - let g1 = g1_to_u256s(g1); - let g2 = g2_to_u256s(self.params.g2()); - let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); - - if separate { - vec![ - ("vk_digest", vk_digest), - ("vk_mptr", U256::from(0)), // dummy vk_mptr - ("vk_len", U256::from(0)), // dummy vk_len - ("num_instances", num_instances), - ("num_advices_user_challenges_offset", U256::from(0)), // dummy num_advices_user_challenges_offset - ("last_quotient_x_cptr", U256::from(0)), // dummy last_quotient_x_cptr - ("first_quotient_x_cptr", U256::from(0)), // dummy first_quotient_x_cptr - ("instance_cptr", U256::from(0)), // dummy instance_cptr - ("k", k), - ("n_inv", n_inv), - ("omega", omega), - ("omega_inv", omega_inv), - ("omega_inv_to_l", omega_inv_to_l), - ("has_accumulator", has_accumulator), - ("acc_offset", acc_offset), - ("num_acc_limbs", num_acc_limbs), - ("num_acc_limb_bits", num_acc_limb_bits), - ("g1_x", g1[0]), - ("g1_y", g1[1]), - ("g2_x_1", g2[0]), - ("g2_x_2", g2[1]), - ("g2_y_1", g2[2]), - ("g2_y_2", g2[3]), - ("neg_s_g2_x_1", neg_s_g2[0]), - ("neg_s_g2_x_2", neg_s_g2[1]), - ("neg_s_g2_y_1", neg_s_g2[2]), - ("neg_s_g2_y_2", neg_s_g2[3]), - ( - "challenges_offset", - U256::from(self.meta.challenge_indices.len() * 32), - ), - ("gate_computations_len_offset", U256::from(0)), // dummy gate_computations_len_offset - ("permutation_computations_len_offset", U256::from(0)), // dummy permutation_computations_len_offset - ("lookup_computations_len_offset", U256::from(0)), // dummy lookup_computations_len_offset - ] - } else { - vec![ - ("vk_digest", vk_digest), - ("num_instances", num_instances), - ("k", k), - ("n_inv", n_inv), - ("omega", omega), - ("omega_inv", omega_inv), - ("omega_inv_to_l", omega_inv_to_l), - ("has_accumulator", has_accumulator), - ("acc_offset", acc_offset), - ("num_acc_limbs", num_acc_limbs), - ("num_acc_limb_bits", num_acc_limb_bits), - ("g1_x", g1[0]), - ("g1_y", g1[1]), - ("g2_x_1", g2[0]), - ("g2_x_2", g2[1]), - ("g2_y_1", g2[2]), - ("g2_y_2", g2[3]), - ("neg_s_g2_x_1", neg_s_g2[0]), - ("neg_s_g2_x_2", neg_s_g2[1]), - ("neg_s_g2_y_1", neg_s_g2[2]), - ("neg_s_g2_y_2", neg_s_g2[3]), - ] - } + // Get the dummy constants using the new function + let mut constants = Self::dummy_vk_constants(separate); + + // Fill in the actual values where applicable + let domain = self.vk.get_domain(); + let vk_digest = fr_to_u256(vk_transcript_repr(self.vk)); + let num_instances = U256::from(self.num_instances); + let k = U256::from(domain.k()); + let n_inv = fr_to_u256(bn256::Fr::from(1 << domain.k()).invert().unwrap()); + let omega = fr_to_u256(domain.get_omega()); + let omega_inv = fr_to_u256(domain.get_omega_inv()); + let omega_inv_to_l = { + let l = self.meta.rotation_last.unsigned_abs() as u64; + fr_to_u256(domain.get_omega_inv().pow_vartime([l])) }; + let has_accumulator = U256::from(self.acc_encoding.is_some() as usize); + let acc_offset = self + .acc_encoding + .map(|acc_encoding| U256::from(acc_encoding.offset)) + .unwrap_or_default(); + let num_acc_limbs = self + .acc_encoding + .map(|acc_encoding| U256::from(acc_encoding.num_limbs)) + .unwrap_or_default(); + let num_acc_limb_bits = self + .acc_encoding + .map(|acc_encoding| U256::from(acc_encoding.num_limb_bits)) + .unwrap_or_default(); + let g1 = self.params.get_g()[0]; + let g1 = g1_to_u256s(g1); + let g2 = g2_to_u256s(self.params.g2()); + let neg_s_g2 = g2_to_u256s(-self.params.s_g2()); + + constants = constants + .into_iter() + .map(|(name, dummy_val)| { + let value = match name { + "vk_digest" => vk_digest, + "num_instances" => num_instances, + "k" => k, + "n_inv" => n_inv, + "omega" => omega, + "omega_inv" => omega_inv, + "omega_inv_to_l" => omega_inv_to_l, + "has_accumulator" => has_accumulator, + "acc_offset" => acc_offset, + "num_acc_limbs" => num_acc_limbs, + "num_acc_limb_bits" => num_acc_limb_bits, + "g1_x" => g1[0], + "g1_y" => g1[1], + "g2_x_1" => g2[0], + "g2_x_2" => g2[1], + "g2_y_1" => g2[2], + "g2_y_2" => g2[3], + "neg_s_g2_x_1" => neg_s_g2[0], + "neg_s_g2_x_2" => neg_s_g2[1], + "neg_s_g2_y_1" => neg_s_g2[2], + "neg_s_g2_y_2" => neg_s_g2[3], + "challenges_offset" => U256::from(self.meta.challenge_indices.len() * 32), + "num_evals" => U256::from(self.meta.num_evals), + "num_neg_lagranges" => { + U256::from(self.meta.rotation_last.unsigned_abs() as usize) + } + _ => dummy_val, + }; + (name, value) + }) + .collect(); + let fixed_comms: Vec<(U256, U256)> = chain![self.vk.fixed_commitments()] .flat_map(g1_to_u256s) .tuples() @@ -289,8 +329,20 @@ impl<'a> SolidityGenerator<'a> { return attached_vk; } + fn set_constant_value(constants: &mut [(&str, U256)], name: &str, value: U256) { + if let Some((_, val)) = constants.iter_mut().find(|(n, _)| *n == name) { + *val = value; + } + } + + let const_expressions = expression_consts(self.vk.cs()) + .into_iter() + .map(fr_to_u256) + .collect::>(); + let vk_mptr_mock = self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); + let dummy_data = Data::new( &self.meta, &attached_vk, @@ -299,21 +351,19 @@ impl<'a> SolidityGenerator<'a> { true, ); - let const_expressions = expression_consts(self.vk.cs()) - .into_iter() - .map(fr_to_u256) - .collect::>(); - let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); + let offset = vk_mptr_mock + (attached_vk.constants.len() * 0x20) + (attached_vk.fixed_comms.len() + attached_vk.permutation_comms.len()) * 0x40; + // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. const_expressions.iter().enumerate().for_each(|(idx, _)| { let mptr = offset + (0x20 * idx); let mptr = Ptr::memory(mptr); vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); }); + let evaluator_dummy = EvaluatorVK::new( self.vk.cs(), &self.meta, @@ -321,20 +371,18 @@ impl<'a> SolidityGenerator<'a> { vk_lookup_const_table_dummy, ); - let instance_cptr = U256::from((self.meta.proof_len(self.scheme)) + 0xa4); - - // set instance_cptr it at position 7 of the constants. - constants[7].1 = instance_cptr; - - let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; - - // set first_quotient_x_cptr at position 6 of the constants. - constants[6].1 = U256::from(first_quotient_x_cptr.value().as_usize()); - - let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); - - // set last_quotient_x_cptr at position 5 of the constants. - constants[5].1 = U256::from(last_quotient_x_cptr.value().as_usize()); + // Fill in the gate computations with dummy values. (maintains the correct shape) + let mut cumulative_length = 0; + let dummy_gate_computations: Vec<(Vec, usize)> = + chain![evaluator_dummy.gate_computations()] + .map(|lines| { + let operations = lines.iter().map(|_line| U256::from(0)).collect::>(); + let length = operations.len(); + let gate_computation = (operations, cumulative_length); + cumulative_length += length; + gate_computation + }) + .collect(); let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); @@ -353,47 +401,54 @@ impl<'a> SolidityGenerator<'a> { }) .collect_vec(); - // Fill in the gate computations with dummy values. (maintains the correct shape) - let mut cumulative_length = 0; - let dummy_gate_computations: Vec<(Vec, usize)> = - chain![evaluator_dummy.gate_computations()] - .map(|lines| { - let operations = lines.iter().map(|_line| U256::from(0)).collect::>(); - let length = operations.len(); - let gate_computation = (operations, cumulative_length); - cumulative_length += length; - gate_computation - }) - .collect(); + // Update constants + let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; + let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); + let instance_cptr = U256::from(self.meta.proof_len(self.scheme) + 0xa4); let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_expressions.len() * 0x20); - - // set the num_advices_user_challenges_offset at position 4 - constants[4] = ( - "num_advices_user_challenges_offset", - U256::from(num_advices_user_challenges_offset), - ); - let gate_computations_len_offset = num_advices_user_challenges_offset + ((num_advices_user_challenges.len() * 0x40) + 0x20); - - // set the gate_computations_len_offset at position 28. - constants[28].1 = U256::from(gate_computations_len_offset); - let permutations_computations_len_offset = gate_computations_len_offset + (0x20 * (dummy_gate_computations.len() + cumulative_length) + 0x20); - - // set the gate_computations_len_offset at position 28. - constants[29].1 = U256::from(permutations_computations_len_offset); - let lookup_computations_len_offset = permutations_computations_len_offset + (0x20 * evaluator_dummy.permutation_computations().len()); - // set the lookup_computations_len_offset at position 30. - constants[30].1 = U256::from(lookup_computations_len_offset); + set_constant_value(&mut constants, "instance_cptr", instance_cptr); + set_constant_value( + &mut constants, + "first_quotient_x_cptr", + U256::from(first_quotient_x_cptr.value().as_usize()), + ); + set_constant_value( + &mut constants, + "last_quotient_x_cptr", + U256::from(last_quotient_x_cptr.value().as_usize()), + ); + set_constant_value( + &mut constants, + "num_advices_user_challenges_offset", + U256::from(num_advices_user_challenges_offset), + ); + set_constant_value( + &mut constants, + "gate_computations_len_offset", + U256::from(gate_computations_len_offset), + ); + set_constant_value( + &mut constants, + "permutation_computations_len_offset", + U256::from(permutations_computations_len_offset), + ); + set_constant_value( + &mut constants, + "lookup_computations_len_offset", + U256::from(lookup_computations_len_offset), + ); + // Recreate the vk with the correct shape let mut vk = Halo2VerifyingKey { constants, fixed_comms, @@ -405,9 +460,16 @@ impl<'a> SolidityGenerator<'a> { permutation_computations: evaluator_dummy.permutation_computations(), lookup_computations: evaluator_dummy.lookup_computations(0), }; + // Now generate the real vk_mptr with a vk that has the correct length let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84)); + // replace the mock vk_mptr with the real vk_mptr + set_constant_value(&mut vk.constants, "vk_mptr", U256::from(vk_mptr)); + // replace the mock vk_len with the real vk_len + let vk_len = vk.len(); + set_constant_value(&mut vk.constants, "vk_len", U256::from(vk_len)); + // Generate the real data. let data = Data::new( &self.meta, @@ -416,18 +478,15 @@ impl<'a> SolidityGenerator<'a> { Ptr::calldata(0x84), true, ); - // replace the mock vk_mptr with the real vk_mptr - vk.constants[1].1 = U256::from(vk_mptr); - // replace the mock vk_len with the real vk_len - let vk_len = vk.len(); - vk.constants[2].1 = U256::from(vk_len); // Regenerate the gate computations with the correct offsets. let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); - // create hashmap of vk.const_expressions values to its vk memory location. + + // create a hashmap of vk.const_expressions values to its vk memory location. let offset = vk_mptr + (vk.constants.len() * 0x20) + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; + // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. vk.const_expressions .iter() @@ -541,6 +600,12 @@ impl<'a> SolidityGenerator<'a> { }); let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); + let dummy_constants = Self::dummy_vk_constants(true); + let vk_const_offsets: HashMap<&'static str, U256> = dummy_constants + .iter() + .enumerate() + .map(|(idx, &(key, _))| (key, U256::from(idx * 32))) + .collect(); // iterate through the quotient_eval_numer_computations and determine longest Vec within the Vec>. // TODO: Use this to estimate static working memory size // let quotient_eval_numer_computations_longest = quotient_eval_numer_computations @@ -560,9 +625,8 @@ impl<'a> SolidityGenerator<'a> { Halo2VerifierReusable { scheme: self.scheme, - num_neg_lagranges: self.meta.rotation_last.unsigned_abs() as usize, - num_evals: self.meta.num_evals, pcs_computations, + vk_const_offsets, } } diff --git a/src/codegen/template.rs b/src/codegen/template.rs index ea34204..86a2b2e 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -4,6 +4,7 @@ use crate::codegen::{ }; use askama::{Error, Template}; use ruint::aliases::U256; +use std::collections::HashMap; use std::fmt; use super::evaluator::{LookupsDataEncoded, PermutationDataEncoded}; @@ -62,9 +63,8 @@ pub(crate) struct Halo2Verifier { #[template(path = "Halo2VerifierReusable.sol")] pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, - pub(crate) num_neg_lagranges: usize, - pub(crate) num_evals: usize, pub(crate) pcs_computations: Vec>, + pub(crate) vk_const_offsets: HashMap<&'static str, U256>, } impl Halo2VerifyingKey { diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 145b222..bb616f0 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -139,16 +139,16 @@ contract Halo2Verifier { function ec_pairing(success, vk_mptr, lhs_x, lhs_y, rhs_x, rhs_y) -> ret { mstore(0x00, lhs_x) mstore(0x20, lhs_y) - mstore(0x40, mload(add(vk_mptr, 0x0260))) - mstore(0x60, mload(add(vk_mptr, 0x0280))) - mstore(0x80, mload(add(vk_mptr, 0x02a0))) - mstore(0xa0, mload(add(vk_mptr, 0x02c0))) + mstore(0x40, mload(add(vk_mptr, {{ vk_const_offsets["g2_x_1"]|hex() }}))) + mstore(0x60, mload(add(vk_mptr, {{ vk_const_offsets["g2_x_2"]|hex() }}))) + mstore(0x80, mload(add(vk_mptr, {{ vk_const_offsets["g2_y_1"]|hex() }}))) + mstore(0xa0, mload(add(vk_mptr, {{ vk_const_offsets["g2_y_2"]|hex() }}))) mstore(0xc0, rhs_x) mstore(0xe0, rhs_y) - mstore(0x100, mload(add(vk_mptr, 0x02e0))) - mstore(0x120, mload(add(vk_mptr, 0x0300))) - mstore(0x140, mload(add(vk_mptr, 0x0320))) - mstore(0x160, mload(add(vk_mptr, 0x0340))) + mstore(0x100, mload(add(vk_mptr, {{ vk_const_offsets["neg_s_g2_x_1"]|hex() }}))) + mstore(0x120, mload(add(vk_mptr, {{ vk_const_offsets["neg_s_g2_x_2"]|hex() }}))) + mstore(0x140, mload(add(vk_mptr, {{ vk_const_offsets["neg_s_g2_y_1"]|hex() }}))) + mstore(0x160, mload(add(vk_mptr, {{ vk_const_offsets["neg_s_g2_y_2"]|hex() }}))) ret := and(success, staticcall(gas(), 0x08, 0x00, 0x180, 0x00, 0x20)) ret := and(ret, mload(0x00)) } @@ -278,13 +278,13 @@ contract Halo2Verifier { // Copy full vk into memory extcodecopy(vk, vk_mptr, 0x00, vk_len) - let instance_cptr := mload(add(vk_mptr, 0xe0)) + let instance_cptr := mload(add(vk_mptr, {{ vk_const_offsets["instance_cptr"]|hex() }})) // Check valid length of proof success := and(success, eq(sub(instance_cptr, 0xa4), calldataload(PROOF_LEN_CPTR))) // Check valid length of instances - let num_instances := mload(add(vk_mptr,0x60)) + let num_instances := mload(add(vk_mptr,{{ vk_const_offsets["num_instances"]|hex() }})) success := and(success, eq(num_instances, calldataload(sub(instance_cptr,0x20)))) // Read instances and witness commitments and generate challenges @@ -304,8 +304,8 @@ contract Halo2Verifier { let proof_cptr := PROOF_CPTR let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory // Set the theta_mptr (vk_mptr + vk_len + challenges_length) - theta_mptr := add(challenge_mptr, mload(add(vk_mptr, 0x0360))) - let num_advices_ptr := add(vk_mptr, mload(add(vk_mptr, 0x80))) + theta_mptr := add(challenge_mptr, mload(add(vk_mptr, {{ vk_const_offsets["challenges_offset"]|hex() }}))) + let num_advices_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) let num_advices_len := mload(num_advices_ptr) let advices_ptr := add(num_advices_ptr, 0x20) // start of advices let challenges_ptr := add(advices_ptr, 0x20) // start of challenges @@ -331,7 +331,7 @@ contract Halo2Verifier { // Read evaluations for - { let proof_cptr_end := add(proof_cptr, {{ (32 * num_evals)|hex() }}) } + { let proof_cptr_end := add(proof_cptr, mul(0x20, mload(add(vk_mptr, {{ vk_const_offsets["num_evals"]|hex() }})))) } // num_evals lt(proof_cptr, proof_cptr_end) {} { @@ -361,11 +361,11 @@ contract Halo2Verifier { extcodecopy(vk, vk_mptr, 0x00, vk_len) // Read accumulator from instances - if mload(add(vk_mptr, 0x01a0)) { - let num_limbs := mload(add(vk_mptr, 0x01e0)) - let num_limb_bits := mload(add(vk_mptr, 0x0200)) + if mload(add(vk_mptr, {{ vk_const_offsets["has_accumulator"]|hex() }})) { + let num_limbs := mload(add(vk_mptr, {{ vk_const_offsets["num_acc_limbs"]|hex() }})) + let num_limb_bits := mload(add(vk_mptr, {{ vk_const_offsets["num_acc_limb_bits"]|hex() }})) - let cptr := add(mload(add(vk_mptr, 0xe0)), mul(mload(add(vk_mptr, 0x01c0)), 0x20)) + let cptr := add(mload(add(vk_mptr, {{ vk_const_offsets["instance_cptr"]|hex() }})), mul(mload(add(vk_mptr, {{ vk_const_offsets["acc_offset"]|hex() }})), 0x20)) let lhs_y_off := mul(num_limbs, 0x20) let rhs_x_off := mul(lhs_y_off, 2) let rhs_y_off := mul(lhs_y_off, 3) @@ -408,7 +408,7 @@ contract Halo2Verifier { // Compute lagrange evaluations and instance evaluation { - let k := mload(add(vk_mptr, 0x100)) + let k := mload(add(vk_mptr, {{ vk_const_offsets["k"]|hex() }})) let x := mload(add(theta_mptr, 0x80)) let x_n := x for @@ -419,16 +419,17 @@ contract Halo2Verifier { x_n := mulmod(x_n, x_n, R) } - let omega := mload(add(vk_mptr, 0x140)) + let omega := mload(add(vk_mptr, {{ vk_const_offsets["omega"]|hex() }})) let x_n_mptr := add(theta_mptr, 0x180) let mptr := x_n_mptr - let num_instances := mload(add(vk_mptr,0x60)) - let mptr_end := add(mptr, mul(0x20, add(num_instances, {{ num_neg_lagranges }}))) + let num_instances := mload(add(vk_mptr, {{ vk_const_offsets["num_instances"]|hex() }})) + let num_neg_lagranges := mload(add(vk_mptr, {{ vk_const_offsets["num_neg_lagranges"]|hex() }})) + let mptr_end := add(mptr, mul(0x20, add(num_instances, num_neg_lagranges))) if iszero(num_instances) { mptr_end := add(mptr_end, 0x20) } for - { let pow_of_omega := mload(add(vk_mptr, 0x180)) } + { let pow_of_omega := mload(add(vk_mptr, {{ vk_const_offsets["omega_inv_to_l"]|hex() }})) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { @@ -440,9 +441,9 @@ contract Halo2Verifier { success := batch_invert(success, x_n_mptr, add(mptr_end, 0x20)) mptr := x_n_mptr - let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, 0x120)),R) + let l_i_common := mulmod(x_n_minus_1, mload(add(vk_mptr, {{ vk_const_offsets["n_inv"]|hex() }})),R) for - { let pow_of_omega := mload(add(vk_mptr, 0x180)) } + { let pow_of_omega := mload(add(vk_mptr, {{ vk_const_offsets["omega_inv_to_l"]|hex() }})) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } { @@ -453,7 +454,7 @@ contract Halo2Verifier { let l_blind := mload(add(x_n_mptr, 0x20)) let l_i_cptr := add(x_n_mptr, 0x40) for - { let l_i_cptr_end := add(x_n_mptr, {{ (num_neg_lagranges * 32)|hex() }}) } + { let l_i_cptr_end := add(x_n_mptr, mul(0x20, num_neg_lagranges)) } lt(l_i_cptr, l_i_cptr_end) { l_i_cptr := add(l_i_cptr, 0x20) } { @@ -463,7 +464,7 @@ contract Halo2Verifier { let instance_eval := 0 for { - let instance_cptr := mload(add(vk_mptr, 0xe0)) + let instance_cptr := mload(add(vk_mptr, {{ vk_const_offsets["instance_cptr"]|hex() }})) let instance_cptr_end := add(instance_cptr, mul(0x20, num_instances)) } lt(instance_cptr, instance_cptr_end) @@ -477,7 +478,7 @@ contract Halo2Verifier { let x_n_minus_1_inv := mload(mptr_end) let l_last := mload(x_n_mptr) - let l_0 := mload(add(x_n_mptr, {{ (num_neg_lagranges * 32)|hex() }})) + let l_0 := mload(add(x_n_mptr, mul(0x20, num_neg_lagranges))) mstore(x_n_mptr, x_n) mstore(add(theta_mptr, 0x1a0), x_n_minus_1_inv) @@ -697,13 +698,13 @@ contract Halo2Verifier { // Compute quotient commitment { - mstore(0x00, calldataload(mload(add(vk_mptr, 0xa0)))) - mstore(0x20, calldataload(add(mload(add(vk_mptr, 0xa0)), 0x20))) + mstore(0x00, calldataload(mload(add(vk_mptr, {{ vk_const_offsets["last_quotient_x_cptr"]|hex() }})))) + mstore(0x20, calldataload(add(mload(add(vk_mptr, {{ vk_const_offsets["last_quotient_x_cptr"]|hex() }})), 0x20))) let x_n := mload(add(theta_mptr, 0x180)) for { - let cptr := sub(mload(add(vk_mptr, 0xa0)), 0x40) - let cptr_end := sub(mload(add(vk_mptr, 0xc0)), 0x40) + let cptr := sub(mload(add(vk_mptr, {{ vk_const_offsets["last_quotient_x_cptr"]|hex() }})), 0x40) + let cptr_end := sub(mload(add(vk_mptr, {{ vk_const_offsets["first_quotient_x_cptr"]|hex() }})), 0x40) } lt(cptr_end, cptr) {} @@ -728,7 +729,7 @@ contract Halo2Verifier { } // Random linear combine with accumulator - if mload(add(vk_mptr, 0x01a0)) { + if mload(add(vk_mptr, {{ vk_const_offsets["first_quotient_x_cptr"]|hex() }})) { mstore(0x00, mload(add(theta_mptr, 0x100))) mstore(0x20, mload(add(theta_mptr, 0x120))) mstore(0x40, mload(add(theta_mptr, 0x140))) From bf8e9f8f052730a934b7b6e33bf471f26973331b Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 23 Jul 2024 23:35:41 -0500 Subject: [PATCH 27/65] *add diagnostic_namespace feat --- rust-toolchain | 2 +- src/lib.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 7f9dbf2..6f79d1d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] channel = "nightly-2024-02-06" -components = ["rustfmt", "clippy"] \ No newline at end of file +components = ["rustfmt", "clippy", "rust-src"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9c6f65d..8cc24ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ //! //! [`halo2`]: http://github.com/privacy-scaling-explorations/halo2 +#![feature(diagnostic_namespace)] #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rustdoc::broken_intra_doc_links)] From 34c27cbfc1a2e84e4dd4c5208665593c73ef1239 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 23 Jul 2024 23:40:18 -0500 Subject: [PATCH 28/65] *update rust toolchain config on yaml. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40ed1c7..40e2036 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,10 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: + profile: minimal toolchain: nightly-2024-02-06 override: true - components: rustfmt, clippy + components: rustfmt, clippy, rust-src - uses: Swatinem/rust-cache@v1 with: From 438164547d10674699a815c7fcae80153d1a98c5 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 23 Jul 2024 23:55:45 -0500 Subject: [PATCH 29/65] Revert "*add diagnostic_namespace feat" This reverts commit bf8e9f8f052730a934b7b6e33bf471f26973331b. --- rust-toolchain | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index 6f79d1d..7f9dbf2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] channel = "nightly-2024-02-06" -components = ["rustfmt", "clippy", "rust-src"] \ No newline at end of file +components = ["rustfmt", "clippy"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 8cc24ee..9c6f65d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ //! //! [`halo2`]: http://github.com/privacy-scaling-explorations/halo2 -#![feature(diagnostic_namespace)] #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(rustdoc::broken_intra_doc_links)] From c4915fd317a0a6822c73c7274fbf9f61e6fb3240 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 00:00:05 -0500 Subject: [PATCH 30/65] *append solc install with hash svm 2>/dev/null --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40e2036..cdffe51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,17 +16,16 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: - profile: minimal toolchain: nightly-2024-02-06 override: true - components: rustfmt, clippy, rust-src + components: rustfmt, clippy - uses: Swatinem/rust-cache@v1 with: cache-on-failure: true - name: Install solc - run: cargo install svm-rs && svm install 0.8.26 && svm use 0.8.26 && solc --version + run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.26 && svm use 0.8.26 && solc --version - name: Run test run: cargo test --workspace --all-features --all-targets -- --nocapture From 428f39ed2e3453e93f5d67affb3125cfb41adb89 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 18:22:34 -0500 Subject: [PATCH 31/65] * updated estimate free static memory fucntion with quotient evaluation memory demands. --- .github/workflows/ci.yml | 7 ++- rust-toolchain | 3 +- src/codegen.rs | 47 +++++++++++--------- src/codegen/evaluator.rs | 92 +++++++++++++++++++++++++++++++++++----- 4 files changed, 112 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdffe51..a1486d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,9 @@ jobs: - name: Install toolchain uses: actions-rs/toolchain@v1 with: - toolchain: nightly-2024-02-06 - override: true - components: rustfmt, clippy - + toolchain: 1.77.2 + profile: minimal + - uses: Swatinem/rust-cache@v1 with: cache-on-failure: true diff --git a/rust-toolchain b/rust-toolchain index 7f9dbf2..fb0f946 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,2 @@ [toolchain] -channel = "nightly-2024-02-06" -components = ["rustfmt", "clippy"] \ No newline at end of file +channel = "1.77.2" \ No newline at end of file diff --git a/src/codegen.rs b/src/codegen.rs index 484fa78..7071a7b 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -341,7 +341,7 @@ impl<'a> SolidityGenerator<'a> { .collect::>(); let vk_mptr_mock = - self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84)); + self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84), false); let dummy_data = Data::new( &self.meta, @@ -462,7 +462,7 @@ impl<'a> SolidityGenerator<'a> { }; // Now generate the real vk_mptr with a vk that has the correct length - let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84)); + let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84), true); // replace the mock vk_mptr with the real vk_mptr set_constant_value(&mut vk.constants, "vk_mptr", U256::from(vk_mptr)); @@ -528,7 +528,7 @@ impl<'a> SolidityGenerator<'a> { let proof_len_cptr = Ptr::calldata(0x6014F51944); let vk = self.generate_vk(false); - let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); + let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr, false); let vk_mptr = Ptr::memory(vk_m); let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, false); @@ -581,7 +581,7 @@ impl<'a> SolidityGenerator<'a> { let proof_cptr = Ptr::calldata(0x84); let vk = self.generate_vk(true); - let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr); + let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr, true); let vk_mptr = Ptr::memory(vk_m); // if separate then create a hashmap of vk.const_expressions values to its vk memory location. let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); @@ -606,17 +606,6 @@ impl<'a> SolidityGenerator<'a> { .enumerate() .map(|(idx, &(key, _))| (key, U256::from(idx * 32))) .collect(); - // iterate through the quotient_eval_numer_computations and determine longest Vec within the Vec>. - // TODO: Use this to estimate static working memory size - // let quotient_eval_numer_computations_longest = quotient_eval_numer_computations - // .iter() - // .max_by_key(|x| x.len()) - // .unwrap() - // .clone(); - // println!( - // "longest computation: {:?}", - // quotient_eval_numer_computations_longest.len() - // ); let pcs_computations = match self.scheme { Bdfg21 => bdfg21_computations(&self.meta, &data, true), @@ -634,12 +623,12 @@ impl<'a> SolidityGenerator<'a> { &self, vk: &Halo2VerifyingKey, proof_cptr: Ptr, + separate: bool, ) -> usize { - // TODO add a check for the amount of memory required for the compute quotient evavluation + let mock_vk_mptr = Ptr::memory(0x100000); + let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr, false); let pcs_computation = match self.scheme { Bdfg21 => { - let mock_vk_mptr = Ptr::memory(0x100000); - let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr, false); let (superset, sets) = rotation_sets(&queries(&self.meta, &mock)); let num_coeffs = sets.iter().map(|set| set.rots().len()).sum::(); 2 * (1 + num_coeffs) + 6 + 2 * superset.len() + 1 + 3 * sets.len() @@ -647,7 +636,7 @@ impl<'a> SolidityGenerator<'a> { Gwc19 => unimplemented!(), }; - itertools::max([ + let mut fsm_usage = itertools::max([ // Keccak256 input (can overwrite vk) itertools::max(chain![ self.meta.num_advices().into_iter().map(|n| n * 2 + 1), @@ -661,7 +650,25 @@ impl<'a> SolidityGenerator<'a> { 12, ]) .unwrap() - * 0x20 + * 0x20; + if separate { + let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); + let const_expressions = expression_consts(self.vk.cs()) + .into_iter() + .map(fr_to_u256) + .collect::>(); + const_expressions.iter().enumerate().for_each(|(idx, _)| { + let mptr = 0x20 * idx; + let mptr = Ptr::memory(mptr); + vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); + }); + let evaluator = + EvaluatorVK::new(self.vk.cs(), &self.meta, &mock, vk_lookup_const_table_dummy); + + let expression_eval_computations = evaluator.quotient_eval_fsm_usage(); + fsm_usage = itertools::max([fsm_usage, expression_eval_computations]).unwrap(); + }; + fsm_usage } } diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 56ed0a4..596f6d9 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -206,7 +206,6 @@ where "let table" ] .map(str::to_string), - // TODO: break this into it's own function on the solidity side of things code_block::<1, false>(chain![ table_lines, [format!("table := {table_0}")], @@ -215,8 +214,6 @@ where )), [format!("table := addmod(table, beta, R)")], ]), - // TODO: break this into it's own function on the solidity side of things, - // calling it within a for loop. izip!(0.., inputs.into_iter()).flat_map(|(idx, (input_lines, inputs))| { let (input_0, rest_inputs) = inputs.split_first().unwrap(); let ident = format!("input_{idx}"); @@ -263,7 +260,6 @@ where ] }), [format!("let lhs"), format!("let rhs")], - // TODO: break this into it's own function on the solidity side of things (0..num_inputs).flat_map(|i| { assert_ne!(num_inputs, 0); if num_inputs == 1 { @@ -284,7 +280,6 @@ where ]) } }), - // TODO: break this into it's own function on the solidity side of things code_block::<1, false>(chain![ [format!("let tmp := input_0")], (1..num_inputs) @@ -696,6 +691,79 @@ where } } + #[cfg(not(feature = "mv-lookup"))] + pub fn quotient_eval_fsm_usage(&self) -> usize { + let gate_computation_longest = chain![self.gate_computations()] + .max_by_key(|x| x.len()) + .unwrap() + .clone() + .len(); + let gate_computation_fsm_usage = gate_computation_longest * 0x20; + + let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; + + // TODO implement the non mv lookup version of this calculation. + let input_expressions_fsm_usage = 0; + + itertools::max([ + gate_computation_fsm_usage, + permutation_computation_fsm_usage, + input_expressions_fsm_usage, + ]) + .unwrap() + } + + #[cfg(feature = "mv-lookup")] + pub fn quotient_eval_fsm_usage(&self) -> usize { + let gate_computation_longest = chain![self.gate_computations()] + .max_by_key(|x| x.len()) + .unwrap() + .clone() + .len(); + let gate_computation_fsm_usage = gate_computation_longest * 0x20; + + // 0x40 offset b/c that is where the fsm pointer starts in the permutations computation code block + let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; + + let evaluate_fsm_usage = |idx: usize, expressions: &Vec<_>| { + let offset = 0xa0; // offset to store theta offset ptrs used + // in the lookup computations. + let fsm = (0x20 * idx) + offset; + self.set_static_mem_ptr(fsm); + let max_fsm_usage = expressions + .iter() + .map(|expression| self.evaluate_encode(expression)) + .fold(fsm, |mut acc, result| { + acc += result.0.len() * 0x20; + acc + }); + self.reset(); + max_fsm_usage + }; + + let input_expressions_fsm_usage = self + .cs + .lookups() + .iter() + .map(|lookup| { + let inputs_iter = lookup.input_expressions().iter().enumerate(); + let fsm_usages = inputs_iter + .clone() + .map(|(idx, expressions)| evaluate_fsm_usage(idx, expressions)) + .collect_vec(); + *fsm_usages.iter().max().unwrap() + }) + .collect_vec(); + let input_expressions_fsm_usage = *input_expressions_fsm_usage.iter().max().unwrap(); + + itertools::max([ + gate_computation_fsm_usage, + permutation_computation_fsm_usage, + input_expressions_fsm_usage, + ]) + .unwrap() + } + #[cfg(feature = "mv-lookup")] pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { let evaluate_table = |expressions: &Vec<_>| { @@ -729,8 +797,6 @@ where acc.1.push(result.1); acc }); - // TODO: Add this free memory pointer - // incrementation to the estimate free static memory function. self.reset(); (lines, inputs) }; @@ -759,12 +825,12 @@ where .map(|(inputs_tables, evals)| { let (inputs, (table_lines, _)) = inputs_tables.clone(); let evals = self.encode_triplet_evaluation_word(evals); - let table_lines = self.encode_pack_ptrs(&table_lines); + let table_lines = self.encode_pack_ptrs(&table_lines).unwrap(); let mut inner_accumulator = 0; let inputs: Vec = inputs .iter() .map(|(input_lines, inputs)| { - let inputs = self.encode_pack_ptrs(inputs); + let inputs = self.encode_pack_ptrs(inputs).unwrap(); let res = InputsEncoded { expression: input_lines.clone(), vars: inputs, @@ -879,12 +945,16 @@ where // pack as many as 16 ptrs into a single word // throws an error if the number of ptrs is greater than 16 - fn encode_pack_ptrs(&self, ptrs: &[U256]) -> U256 { + fn encode_pack_ptrs(&self, ptrs: &[U256]) -> Result { + if ptrs.len() > 16 { + return Err("Number of pointers cannot be greater than 16"); + } + let mut packed = U256::from(0); for (i, ptr) in ptrs.iter().enumerate() { packed |= *ptr << (i * 16); } - packed + Ok(packed) } fn evaluate_and_reset(&self, expression: &Expression) -> Vec { From 78456c4f90022725759f14c07d7c0d18ea14bea5 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 24 Jul 2024 22:59:07 -0500 Subject: [PATCH 32/65] gate_computations struct. --- src/codegen.rs | 50 +++++++------------------- src/codegen/evaluator.rs | 56 ++++++++++++++++++++++++++--- src/codegen/template.rs | 10 ++---- src/codegen/util.rs | 3 +- templates/Halo2VerifierReusable.sol | 2 +- templates/Halo2VerifyingKey.sol | 22 ++++++------ 6 files changed, 80 insertions(+), 63 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 7071a7b..28df996 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -9,7 +9,7 @@ use crate::codegen::{ expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, }, }; -use evaluator::{LookupsDataEncoded, PermutationDataEncoded}; +use evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}; use halo2_proofs::{ halo2curves::{bn256, ff::Field}, plonk::VerifyingKey, @@ -319,8 +319,7 @@ impl<'a> SolidityGenerator<'a> { permutation_comms: permutation_comms.clone(), const_expressions: vec![], num_advices_user_challenges: vec![], - gate_computations: vec![], - gate_computations_total_length: 0, + gate_computations: GateDataEncoded::default(), permutation_computations: PermutationDataEncoded::default(), lookup_computations: LookupsDataEncoded::default(), }; @@ -372,17 +371,9 @@ impl<'a> SolidityGenerator<'a> { ); // Fill in the gate computations with dummy values. (maintains the correct shape) - let mut cumulative_length = 0; - let dummy_gate_computations: Vec<(Vec, usize)> = - chain![evaluator_dummy.gate_computations()] - .map(|lines| { - let operations = lines.iter().map(|_line| U256::from(0)).collect::>(); - let length = operations.len(); - let gate_computation = (operations, cumulative_length); - cumulative_length += length; - gate_computation - }) - .collect(); + let gate_computations_dummy = evaluator_dummy.gate_computations(); + let permutation_computations_dummy = evaluator_dummy.permutation_computations(); + let lookup_computations_dummy = evaluator_dummy.lookup_computations(0); let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); @@ -411,10 +402,10 @@ impl<'a> SolidityGenerator<'a> { + (const_expressions.len() * 0x20); let gate_computations_len_offset = num_advices_user_challenges_offset + ((num_advices_user_challenges.len() * 0x40) + 0x20); - let permutations_computations_len_offset = gate_computations_len_offset - + (0x20 * (dummy_gate_computations.len() + cumulative_length) + 0x20); - let lookup_computations_len_offset = permutations_computations_len_offset - + (0x20 * evaluator_dummy.permutation_computations().len()); + let permutations_computations_len_offset = + gate_computations_len_offset + (0x20 * gate_computations_dummy.len()); + let lookup_computations_len_offset = + permutations_computations_len_offset + (0x20 * permutation_computations_dummy.len()); set_constant_value(&mut constants, "instance_cptr", instance_cptr); set_constant_value( @@ -455,10 +446,9 @@ impl<'a> SolidityGenerator<'a> { permutation_comms, const_expressions, num_advices_user_challenges, - gate_computations: dummy_gate_computations, - gate_computations_total_length: cumulative_length, - permutation_computations: evaluator_dummy.permutation_computations(), - lookup_computations: evaluator_dummy.lookup_computations(0), + gate_computations: gate_computations_dummy, + permutation_computations: permutation_computations_dummy, + lookup_computations: lookup_computations_dummy, }; // Now generate the real vk_mptr with a vk that has the correct length @@ -500,22 +490,8 @@ impl<'a> SolidityGenerator<'a> { // Now we initalize the real evaluator_vk which will contain the correct offsets in the vk_lookup_const_table. let evaluator = EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table); - let mut cumulative_length = 0; - let gate_computations: Vec<(Vec, usize)> = chain![evaluator.gate_computations()] - .map(|lines| { - let operations = lines - .iter() - .map(|line: &ruint::Uint<256, 4>| U256::from(*line)) - .collect::>(); - let length = operations.len(); - let gate_computation = (operations, cumulative_length); - cumulative_length += length; - gate_computation - }) - .collect(); - // NOTE: We don't need to replace the gate_computations_total_length since we are only potentially modifying the offsets for each constant mload operation. - vk.gate_computations = gate_computations; + vk.gate_computations = evaluator.gate_computations(); // We need to replace the lookup_computations so that the constant mptrs in the encoded input expessions have the correct offsets. vk.lookup_computations = evaluator.lookup_computations(vk_mptr + lookup_computations_len_offset); diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 596f6d9..335ec44 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -531,6 +531,36 @@ pub enum OperandMem { StaticMemory, } +#[derive(Clone, PartialEq, Eq)] +pub struct GateEncoded { + pub(crate) expression: Vec, + pub(crate) acc: usize, +} + +impl GateEncoded { + pub fn len(&self) -> usize { + self.expression.len() + } +} + +// Holds the encoded data stored in the separate VK +// needed to perform the gate computations of +// the quotient evaluation portion of the reusable verifier. +#[derive(Clone, PartialEq, Eq, Default)] +pub struct GateDataEncoded { + pub(crate) gates: Vec, +} + +impl GateDataEncoded { + pub fn len(&self) -> usize { + if self == &Self::default() { + 0 + } else { + 1 + self.gates.len() + self.gates.iter().map(GateEncoded::len).sum::() + } + } +} + // Holds the encoded data stored in the separate VK // needed to perform the permutation computations of // the quotient evaluation portion of the reusable verifier. @@ -650,13 +680,29 @@ where } } - pub fn gate_computations(&self) -> Vec> { - self.cs + pub fn gate_computations(&self) -> GateDataEncoded { + let res: Vec> = self + .cs .gates() .iter() .flat_map(Gate::polynomials) .map(|expression| self.evaluate_and_reset(expression)) - .collect() + .collect(); + let mut cumulative_length = 0; + let gate_computations: Vec = chain![res] + .map(|lines| { + let length = lines.len(); + let gate_computation = GateEncoded { + expression: lines, + acc: cumulative_length, + }; + cumulative_length += length; + gate_computation + }) + .collect(); + GateDataEncoded { + gates: gate_computations, + } } pub fn permutation_computations(&self) -> PermutationDataEncoded { @@ -693,7 +739,7 @@ where #[cfg(not(feature = "mv-lookup"))] pub fn quotient_eval_fsm_usage(&self) -> usize { - let gate_computation_longest = chain![self.gate_computations()] + let gate_computation_longest = chain![self.gate_computations().gates] .max_by_key(|x| x.len()) .unwrap() .clone() @@ -715,7 +761,7 @@ where #[cfg(feature = "mv-lookup")] pub fn quotient_eval_fsm_usage(&self) -> usize { - let gate_computation_longest = chain![self.gate_computations()] + let gate_computation_longest = chain![self.gate_computations().gates] .max_by_key(|x| x.len()) .unwrap() .clone() diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 86a2b2e..525a786 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -7,7 +7,7 @@ use ruint::aliases::U256; use std::collections::HashMap; use std::fmt; -use super::evaluator::{LookupsDataEncoded, PermutationDataEncoded}; +use super::evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}; #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] @@ -17,8 +17,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_expressions: Vec, - pub(crate) gate_computations: Vec<(Vec, usize)>, - pub(crate) gate_computations_total_length: usize, + pub(crate) gate_computations: GateDataEncoded, pub(crate) permutation_computations: PermutationDataEncoded, pub(crate) lookup_computations: LookupsDataEncoded, } @@ -29,10 +28,7 @@ impl Halo2VerifyingKey { + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + (self.const_expressions.len() * 0x20) + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) - // The length words of the inner vector + length word of the outer vector - + (self.gate_computations.len() * 0x20 + 0x20) - // Sum up the lengths of al the nested vectors - + (self.gate_computations_total_length * 0x20) + + (self.gate_computations.len() * 0x20) + (self.permutation_computations.len() * 0x20) + (self.lookup_computations.len() * 0x20) } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 6ae4d6b..15853ea 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -402,8 +402,7 @@ impl Data { + (2 * vk.permutation_comms.len()) + vk.const_expressions.len() + (2 * vk.num_advices_user_challenges.len() + 1) - + (vk.gate_computations.len() + 1) - + (vk.gate_computations_total_length) + + (vk.gate_computations.len()) + (vk.permutation_computations.len()) + (vk.lookup_computations.len()); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index bb616f0..eb7a49a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -571,7 +571,7 @@ contract Halo2Verifier { } } { - // Lookup computations + // MV lookup computations mstore(0x0, mload(add(theta_mptr, 0x1C0))) // l_last mstore(0x20, mload(add(theta_mptr, 0x200))) // l_0 mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 3082b60..3648ff9 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -30,22 +30,22 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.len())|hex_padded(64) }}) // gate_computations length - {%- for (gate_computation, acc) in gate_computations %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.gates.len())|hex_padded(64) }}) // gate_computations length + {%- for gate in gate_computations.gates %} {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} - {%- let offset = base_offset + loop.index0 + acc %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computation.len())|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] - {%- for operation in gate_computation %} + {%- let offset = base_offset + loop.index0 + gate.acc %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate.expression.len())|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] + {%- for operation in gate.expression %} {%- let offset = offset + loop.index0 + 1 %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ operation|hex_padded(64) }}) // gate_computation[{{ loop.index0 }}] {%- endfor %} {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.z_evals_last_idx|hex_padded(64) }}) // z_evals_last_idx - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 3 + gate_computations.len() + gate_computations_total_length %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.chunk_offset|hex_padded(64) }}) // chunk_offset {%- for z_eval in permutation_computations.permutation_z_evals %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 4 + gate_computations.len() + gate_computations_total_length %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 3 + gate_computations.len() %} {%- let offset = base_offset + loop.index0 * (permutation_computations.column_evals[0].len() + 1)%} mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_evals[{{ loop.index0 }}] {%- let last_index = permutation_computations.permutation_z_evals.len() - 1 %} @@ -62,10 +62,10 @@ contract Halo2VerifyingKey { mstore({{ (32 * offset)|hex_padded(4) }}, {{ column_eval|hex_padded(64) }}) // column_eval[{{ loop.index0 }}] {%- endfor %} {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations {%- for lookup in lookup_computations.lookups %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() + 1 %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + permutation_computations.len() %} {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] mstore({{ (32 * (offset + 1))|hex_padded(4) }}, {{ lookup.table_lines|hex_padded(64) }}) // lookup_table_lines[{{ loop.index0 }}] @@ -79,7 +79,7 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + gate_computations_total_length + permutation_computations.len() + lookup_computations.len() ))|hex() }}) + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() ))|hex() }}) } } } From c6a19a54c36ec08297c8a22c9ea5e81b907c6dc2 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 26 Jul 2024 15:03:25 -0500 Subject: [PATCH 33/65] *packed expression words gate computations --- src/codegen.rs | 3 +- src/codegen/evaluator.rs | 112 +++++++++++++++++----------- src/test.rs | 2 +- templates/Halo2VerifierReusable.sol | 68 ++++++++++++++--- templates/Halo2VerifyingKey.sol | 14 ++-- 5 files changed, 133 insertions(+), 66 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 28df996..0c6c2f3 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -576,8 +576,7 @@ impl<'a> SolidityGenerator<'a> { }); let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); - let dummy_constants = Self::dummy_vk_constants(true); - let vk_const_offsets: HashMap<&'static str, U256> = dummy_constants + let vk_const_offsets: HashMap<&'static str, U256> = Self::dummy_vk_constants(true) .iter() .enumerate() .map(|(idx, &(key, _))| (key, U256::from(idx * 32))) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 335ec44..2227900 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -531,24 +531,13 @@ pub enum OperandMem { StaticMemory, } -#[derive(Clone, PartialEq, Eq)] -pub struct GateEncoded { - pub(crate) expression: Vec, - pub(crate) acc: usize, -} - -impl GateEncoded { - pub fn len(&self) -> usize { - self.expression.len() - } -} - // Holds the encoded data stored in the separate VK // needed to perform the gate computations of // the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq, Default)] pub struct GateDataEncoded { - pub(crate) gates: Vec, + pub(crate) length: usize, + pub(crate) packed_expression_words: Vec, } impl GateDataEncoded { @@ -556,7 +545,7 @@ impl GateDataEncoded { if self == &Self::default() { 0 } else { - 1 + self.gates.len() + self.gates.iter().map(GateEncoded::len).sum::() + 1 + self.packed_expression_words.len() } } } @@ -681,30 +670,39 @@ where } pub fn gate_computations(&self) -> GateDataEncoded { - let res: Vec> = self + let packed_expression_words: Vec> = self .cs .gates() .iter() .flat_map(Gate::polynomials) - .map(|expression| self.evaluate_and_reset(expression)) - .collect(); - let mut cumulative_length = 0; - let gate_computations: Vec = chain![res] - .map(|lines| { - let length = lines.len(); - let gate_computation = GateEncoded { - expression: lines, - acc: cumulative_length, - }; - cumulative_length += length; - gate_computation - }) + .map(|expression| self.evaluate_and_reset(expression, true)) .collect(); + let length = packed_expression_words.len(); + let packed_expression_words_flattened: Vec = + packed_expression_words.into_iter().flatten().collect(); + GateDataEncoded { - gates: gate_computations, + length, + packed_expression_words: packed_expression_words_flattened, } } + pub fn gate_computation_fsm_usage(&self) -> usize { + let packed_expression_words: Vec> = self + .cs + .gates() + .iter() + .flat_map(Gate::polynomials) + .map(|expression| self.evaluate_and_reset(expression, false)) + .collect(); + let gate_computation_longest = chain![packed_expression_words] + .max_by_key(|x| x.len()) + .unwrap() + .clone() + .len(); + gate_computation_longest * 0x20 + } + pub fn permutation_computations(&self) -> PermutationDataEncoded { let Self { meta, data, .. } = self; let permutation_z_evals_last_idx = 32 * (data.permutation_z_evals.len() - 1); @@ -739,12 +737,7 @@ where #[cfg(not(feature = "mv-lookup"))] pub fn quotient_eval_fsm_usage(&self) -> usize { - let gate_computation_longest = chain![self.gate_computations().gates] - .max_by_key(|x| x.len()) - .unwrap() - .clone() - .len(); - let gate_computation_fsm_usage = gate_computation_longest * 0x20; + let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; @@ -761,12 +754,7 @@ where #[cfg(feature = "mv-lookup")] pub fn quotient_eval_fsm_usage(&self) -> usize { - let gate_computation_longest = chain![self.gate_computations().gates] - .max_by_key(|x| x.len()) - .unwrap() - .clone() - .len(); - let gate_computation_fsm_usage = gate_computation_longest * 0x20; + let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); // 0x40 offset b/c that is where the fsm pointer starts in the permutations computation code block let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; @@ -1003,11 +991,49 @@ where Ok(packed) } - fn evaluate_and_reset(&self, expression: &Expression) -> Vec { + fn encode_pack_expr_operations(&self, exprs: Vec) -> Vec { + let mut packed_words: Vec = vec![U256::from(0)]; + let mut bit_counter = 8; + let mut last_idx = 0; + + for expr in exprs.iter() { + let first_byte = expr.as_limbs()[0] & 0xFF; + let offset = if first_byte == 0 || first_byte == 1 { + 24 + } else { + 40 + }; + + let mut next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + next_bit_counter = offset; + packed_words[last_idx] = *expr + } else { + packed_words[last_idx] |= *expr << bit_counter; + } + bit_counter = next_bit_counter; + } + + let packed_words_len = packed_words.len(); + + // Encode the length of the exprs vec in the first word + packed_words[0] |= U256::from(packed_words_len); + + packed_words + } + + fn evaluate_and_reset(&self, expression: &Expression, pack: bool) -> Vec { *self.static_mem_ptr.borrow_mut() = 0x0; let result = self.evaluate_encode(expression); self.reset(); - result.0 + let res = result.0; + if pack { + self.encode_pack_expr_operations(res) + } else { + res + } } fn set_static_mem_ptr(&self, value: usize) { diff --git a/src/test.rs b/src/test.rs index 2b66bb7..32cb358 100644 --- a/src/test.rs +++ b/src/test.rs @@ -100,7 +100,7 @@ fn run_render_separately>() { // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); // print vk_solidity - // println!("VK solidity: {vk_solidity}"); + println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); let vk_address = evm.create(vk_creation_code); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index eb7a49a..0a77e31 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -229,6 +229,60 @@ contract Halo2Verifier { ret := quotient_eval_numer } + function expression_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { + // Load in the least significant byte of the `expressions_word` word to get the total number of words we will need to load in. + let num_words := add(mul(0x20, and(expressions_word, 0xFF)), 0x20) + // start of the expression encodings + expressions_word := shr(8, expressions_word) + let acc + for { let i := 0x20 } lt(i, num_words) { i := add(i, 0x20) } { + for { } expressions_word { } { + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expressions_word, 0xFF) + // 0x00 => Advice/Fixed expression + case 0x00 { + expressions_word := shr(8, expressions_word) + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(add(fsmp, acc), calldataload(and(expressions_word, 0xFFFF))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x01 => Negated expression + case 0x01 { + expressions_word := shr(8, expressions_word) + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(add(fsmp, acc), sub(R, mload(and(expressions_word, 0xFFFF)))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x02 => Sum expression + case 0x02 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(add(fsmp, acc), addmod(mload(and(expressions_word, 0xFFFF)), mload(and(shr(16, expressions_word), 0xFFFF)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + // 0x03 => Product/scalar expression + case 0x03 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(add(fsmp, acc), mulmod(mload(and(expressions_word, 0xFFFF)),mload(and(shr(16, expressions_word), 0xFFFF)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + acc := add(acc, 0x20) + } + ret0 := add(code_ptr, i) + expressions_word := mload(ret0) + } + ret0 := ret0 + ret1 := expressions_word + ret2 := sub(acc, 0x20) + } function expression_evals(fsmp, code_len, code_ptr) { for { let i := 0 } lt(i, code_len) { i := add(i, 0x20) } { /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word @@ -500,22 +554,14 @@ contract Halo2Verifier { { // Gate computations / expression evaluations. let computations_ptr, computations_len := soa_layout_metadata(0x380, vk_mptr) - let expression := 0x0 // Initialize this to 0. Will set it later in the loop. Expression represent the operation type and assocaited operand pointers. - let expression_acc := 0x0 + let expressions_word := mload(computations_ptr) + let last_idx // Load in the total number of code blocks from the vk constants, right after the number challenges for { let code_block := 0 } lt(code_block, computations_len) { code_block := add(code_block, 0x20) } { - let code_ptr := add(add(computations_ptr, code_block), expression_acc) - // Shift the code_len by the free_static_memory_ptr - let code_len := mload(code_ptr) // call expression_evals to evaluate the expressions in the code block - expression_evals(0x00, code_len, add(code_ptr, 0x20)) - - expression_acc := add(expression_acc, code_len) - - let last_idx := sub(code_len, 0x20) + computations_ptr, expressions_word, last_idx := expression_evals_packed(0x00, computations_ptr, expressions_word) // at the end of each code block we update `quotient_eval_numer` - // If this is the first code block, we set `quotient_eval_numer` to the last var in the code block switch eq(code_block, 0) case 1 { diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 3648ff9..27ca942 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -30,15 +30,11 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y {%- endfor %} {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.gates.len())|hex_padded(64) }}) // gate_computations length - {%- for gate in gate_computations.gates %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length + {%- for packed_expression_word in gate_computations.packed_expression_words %} {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} - {%- let offset = base_offset + loop.index0 + gate.acc %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate.expression.len())|hex_padded(64) }}) // gate_computation length[{{ loop.index0 }}] - {%- for operation in gate.expression %} - {%- let offset = offset + loop.index0 + 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ operation|hex_padded(64) }}) // gate_computation[{{ loop.index0 }}] - {%- endfor %} + {%- let offset = base_offset + loop.index0 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ packed_expression_word|hex_padded(64) }}) // packed_expression_word [{{ loop.index0 }}] {%- endfor %} {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.z_evals_last_idx|hex_padded(64) }}) // z_evals_last_idx @@ -79,7 +75,7 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() ))|hex() }}) + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() ))|hex() }}) } } } From 4c25caa2eacaab37f8cd28c175cc95000833b01b Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 26 Jul 2024 22:13:46 -0500 Subject: [PATCH 34/65] *revert to old solc version --- .github/workflows/ci.yml | 2 +- templates/Halo2Verifier.sol | 2 +- templates/Halo2VerifierReusable.sol | 3 +-- templates/Halo2VerifyingKey.sol | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1486d8..a407832 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: cache-on-failure: true - name: Install solc - run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.26 && svm use 0.8.26 && solc --version + run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.20 && svm use 0.8.20 && solc --version - name: Run test run: cargo test --workspace --all-features --all-targets -- --nocapture diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 8a42ef5..279a551 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.26; +pragma solidity ^0.8.0; contract Halo2Verifier { uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 0a77e31..3dc92c4 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.26; +pragma solidity ^0.8.0; contract Halo2Verifier { uint256 internal constant PROOF_LEN_CPTR = 0x64; @@ -279,7 +279,6 @@ contract Halo2Verifier { ret0 := add(code_ptr, i) expressions_word := mload(ret0) } - ret0 := ret0 ret1 := expressions_word ret2 := sub(acc, 0x20) } diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 27ca942..bb8f49f 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.26; +pragma solidity ^0.8.0; contract Halo2VerifyingKey { constructor() { From 0654a72b007e8002c2a3eede3565205ea560311a Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 28 Jul 2024 17:59:40 -0500 Subject: [PATCH 35/65] *pack lookup input and table expressions. --- src/codegen/evaluator.rs | 106 +++++----------------- templates/Halo2VerifierReusable.sol | 131 ++++++++++++++-------------- templates/Halo2VerifyingKey.sol | 8 +- 3 files changed, 91 insertions(+), 154 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 2227900..1524cd1 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -246,7 +246,6 @@ where } else { input_lines }; - // use regex to chain![ [format!("let {ident}")], code_block::<1, false>(chain![ @@ -596,28 +595,31 @@ pub struct InputsEncoded { #[derive(Clone, PartialEq, Eq)] pub struct LookupEncoded { pub(crate) evals: U256, - pub(crate) table_lines: U256, + pub(crate) table_lines: Vec, + pub(crate) table_inputs: U256, pub(crate) acc: usize, pub(crate) inputs: Vec, } // For each element of the lookups vector we have a word for: // 1) the evals (cptr, cptr, cptr), -// 2) table lines Vec packed into a single (throws an error if table_lines.len() > 16) -// 3) outer_inputs_len (inputs.0.len()) +// 2) table_lines Vec +// 3) table_inputs Vec packed into a single (throws an error if table_inputs.len() > 16) +// 4) outer_inputs_len (inputs.0.len()) // For each element of the inputs vector in LookupEncoded we have a word for: -// 1) input_lines_len (inputs[i].0.len()) packed into a single (throws an error if inputs.1.len() > 16) -// 2) inputs (inputs[i].1) packed into a single (throws an error if inputs.1.len() > 16) +// 1) inputs (inputs[i].expressions) +// 2) input_vars Vec packed into a single (throws an error if > 16) // Then we have a word for each step in the expression evaluation. This is the // sum of the lengths of the inputs. impl LookupEncoded { pub fn len(&self) -> usize { - 3 + (2 * self.inputs.len()) + 3 + (self.inputs.len()) + self .inputs .iter() .map(|inputs| inputs.expression.len()) .sum::() + + self.table_lines.len() } } @@ -801,20 +803,20 @@ where #[cfg(feature = "mv-lookup")] pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { let evaluate_table = |expressions: &Vec<_>| { - // println!("expressions: {:?}", expressions); + let offset = 0xa0; // offset to store theta offset ptrs used + self.set_static_mem_ptr(offset); // println!("expressions: {:?}", expressions); let (lines, inputs) = expressions .iter() - .map(|expression| self.evaluate_encode_calldata(expression)) + .map(|expression| self.evaluate_encode(expression)) .fold((Vec::new(), Vec::new()), |mut acc, result| { acc.0.extend(result.0); acc.1.push(result.1); acc }); - // assert that lines.len() <= and inputs.len() == inputs.len() - assert!(lines.len() <= 16); - assert!(inputs.len() == lines.len()); + assert!(inputs.len() <= 16); self.reset(); - (lines, inputs) + let lines_packed = self.encode_pack_expr_operations(lines); + (lines_packed, inputs) }; let evaluate_inputs = |idx: usize, expressions: &Vec<_>| { @@ -832,7 +834,8 @@ where acc }); self.reset(); - (lines, inputs) + let lines_packed = self.encode_pack_expr_operations(lines); + (lines_packed, inputs) }; let inputs_tables = self @@ -857,9 +860,9 @@ where let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) .map(|(inputs_tables, evals)| { - let (inputs, (table_lines, _)) = inputs_tables.clone(); + let (inputs, (table_lines, table_inputs)) = inputs_tables.clone(); let evals = self.encode_triplet_evaluation_word(evals); - let table_lines = self.encode_pack_ptrs(&table_lines).unwrap(); + let table_inputs = self.encode_pack_ptrs(&table_inputs).unwrap(); let mut inner_accumulator = 0; let inputs: Vec = inputs .iter() @@ -876,7 +879,8 @@ where .collect_vec(); let lookup_encoded = LookupEncoded { evals, - table_lines, + table_lines: table_lines.clone(), + table_inputs, inputs: inputs.clone(), acc: accumulator, }; @@ -902,29 +906,6 @@ where LookupsDataEncoded::default() } - fn eval_encode_raw_cptr( - &self, - column_type: impl Into, - column_index: usize, - rotation: i32, - ) -> U256 { - match column_type.into() { - Any::Advice(_) => U256::from( - self.data.advice_evals[&(column_index, rotation)] - .ptr() - .value() - .as_usize(), - ), - Any::Fixed => U256::from( - self.data.fixed_evals[&(column_index, rotation)] - .ptr() - .value() - .as_usize(), - ), - Any::Instance => unimplemented!(), // On the EVM side the 0x0 op here we will inidicate that we need to perform the l_0 mload operation. - } - } - fn eval_encoded( &self, column_type: impl Into, @@ -1040,28 +1021,6 @@ where *self.static_mem_ptr.borrow_mut() = value; } - fn evaluate_encode_calldata(&self, expression: &Expression) -> (Vec, U256) { - evaluate_calldata( - expression, - &|query| { - self.init_encoded_var( - self.eval_encode_raw_cptr(Fixed, query.column_index(), query.rotation().0), - OperandMem::Calldata, - ) - }, - &|query| { - self.init_encoded_var( - self.eval_encode_raw_cptr( - Advice::default(), - query.column_index(), - query.rotation().0, - ), - OperandMem::Calldata, - ) - }, - ) - } - fn evaluate_encode(&self, expression: &Expression) -> (Vec, U256) { evaluate( expression, @@ -1225,26 +1184,3 @@ where Expression::Scaled(value, scalar) => scaled(evaluate(value), fe_to_u256(*scalar)), } } - -#[allow(clippy::too_many_arguments)] -fn evaluate_calldata( - expression: &Expression, - fixed: &impl Fn(FixedQuery) -> T, - advice: &impl Fn(AdviceQuery) -> T, -) -> T -where - F: PrimeField, -{ - match expression { - Expression::Constant(_) => unreachable!(), - Expression::Selector(_) => unreachable!(), - Expression::Fixed(query) => fixed(*query), - Expression::Advice(query) => advice(*query), - Expression::Instance(_) => unreachable!(), - Expression::Challenge(_) => unreachable!(), - Expression::Negated(_) => unreachable!(), - Expression::Sum(_, _) => unreachable!(), - Expression::Product(_, _) => unreachable!(), - Expression::Scaled(_, _) => unreachable!(), - } -} diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 3dc92c4..66a3222 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -229,6 +229,48 @@ contract Halo2Verifier { ret := quotient_eval_numer } + function expression_operations(expressions_word, fsmp, acc) -> ret { + let mstore_ptr := add(fsmp, acc) + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expressions_word, 0xFF) + // 0x00 => Advice/Fixed expression + case 0x00 { + expressions_word := shr(8, expressions_word) + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(mstore_ptr, calldataload(and(expressions_word, 0xFFFF))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x01 => Negated expression + case 0x01 { + expressions_word := shr(8, expressions_word) + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(mstore_ptr, sub(R, mload(and(expressions_word, 0xFFFF)))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x02 => Sum expression + case 0x02 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(mstore_ptr, addmod(mload(and(expressions_word, 0xFFFF)), mload(and(shr(16, expressions_word), 0xFFFF)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + // 0x03 => Product/scalar expression + case 0x03 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(mstore_ptr, mulmod(mload(and(expressions_word, 0xFFFF)),mload(and(shr(16, expressions_word), 0xFFFF)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + ret := expressions_word + } + function expression_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { // Load in the least significant byte of the `expressions_word` word to get the total number of words we will need to load in. let num_words := add(mul(0x20, and(expressions_word, 0xFF)), 0x20) @@ -237,43 +279,7 @@ contract Halo2Verifier { let acc for { let i := 0x20 } lt(i, num_words) { i := add(i, 0x20) } { for { } expressions_word { } { - // Load in the least significant byte of the `expression` word to get the operation type - // Then determine which operation to peform and then store the result in the next available memory slot. - switch and(expressions_word, 0xFF) - // 0x00 => Advice/Fixed expression - case 0x00 { - expressions_word := shr(8, expressions_word) - // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - mstore(add(fsmp, acc), calldataload(and(expressions_word, 0xFFFF))) - // Move to the next expression - expressions_word := shr(16, expressions_word) - } - // 0x01 => Negated expression - case 0x01 { - expressions_word := shr(8, expressions_word) - // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - mstore(add(fsmp, acc), sub(R, mload(and(expressions_word, 0xFFFF)))) - // Move to the next expression - expressions_word := shr(16, expressions_word) - } - // 0x02 => Sum expression - case 0x02 { - expressions_word := shr(8, expressions_word) - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(add(fsmp, acc), addmod(mload(and(expressions_word, 0xFFFF)), mload(and(shr(16, expressions_word), 0xFFFF)),R)) - // Move to the next expression - expressions_word := shr(32, expressions_word) - } - // 0x03 => Product/scalar expression - case 0x03 { - expressions_word := shr(8, expressions_word) - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(add(fsmp, acc), mulmod(mload(and(expressions_word, 0xFFFF)),mload(and(shr(16, expressions_word), 0xFFFF)),R)) - // Move to the next expression - expressions_word := shr(32, expressions_word) - } + expressions_word := expression_operations(expressions_word, fsmp, acc) acc := add(acc, 0x20) } ret0 := add(code_ptr, i) @@ -282,6 +288,25 @@ contract Halo2Verifier { ret1 := expressions_word ret2 := sub(acc, 0x20) } + + function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word, theta, beta) -> ret0, ret1 { + let idx + let vars // packed vars with mload cptrs to load in the vars stored in static memory in the + // expression evaluation. + ret0, vars, idx := expression_evals_packed(fsmp, code_ptr, expressions_word) + ret1 := mload(and(vars, 0xFFFF)) // initlaize the accumulator with the first value in the vars + vars := shr(16, vars) + for { } vars { } { + ret1 := addmod( + mulmod(ret1, theta, R), + mload(and(vars, 0xFFFF)), + R + ) + vars := shr(16, vars) + } + ret1 := addmod(ret1, beta, R) + } + function expression_evals(fsmp, code_len, code_ptr) { for { let i := 0 } lt(i, code_len) { i := add(i, 0x20) } { /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word @@ -640,40 +665,14 @@ contract Halo2Verifier { let table // load in the table_lines_len from the evals_ptr evals_ptr := add(evals_ptr, 0x20) - let table_lines := mload(evals_ptr) - table := calldataload(and(table_lines, 0xFFFF)) - table_lines := shr(16, table_lines) - for { } table_lines { } { - // extract the calldata ptr from the tables_lines - table := addmod( - mulmod(table, mload(0x60), R), - calldataload(and(table_lines, 0xFFFF)), - R - ) - table_lines := shr(16, table_lines) - } - table := addmod(table, mload(0x80), R) + evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) evals_ptr := add(evals_ptr, 0x20) let outer_inputs_len := mload(evals_ptr) for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { evals_ptr := add(evals_ptr, 0x20) - let input_lines_len := mload(evals_ptr) // call the expression_evals function to evaluate the input_lines - expression_evals(j, input_lines_len, add(evals_ptr, 0x20)) - evals_ptr := add(add(evals_ptr, input_lines_len), 0x20) - let inputs := mload(evals_ptr) - let ident := mload(and(inputs, 0xFFFF)) - inputs := shr(16, inputs) - for { } inputs { } { - // extract the mload ptr from the inputs stored in memory - ident := addmod( - mulmod(ident, mload(0x60), R), - mload(and(inputs, 0xFFFF)), - R - ) - inputs := shr(16, inputs) - } - ident := addmod(ident, mload(0x80), R) + let ident + evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) // store ident in free static memory mstore(j, ident) } diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index bb8f49f..389b9af 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -64,11 +64,13 @@ contract Halo2VerifyingKey { {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + permutation_computations.len() %} {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] - mstore({{ (32 * (offset + 1))|hex_padded(4) }}, {{ lookup.table_lines|hex_padded(64) }}) // lookup_table_lines[{{ loop.index0 }}] - mstore({{ (32 * (offset + 2))|hex_padded(4) }}, {{ (32 * lookup.inputs.len())|hex_padded(64) }}) // outer_inputs_len[{{ loop.index0 }}] + {%- for table_line in lookup.table_lines %} + mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] + {%- endfor %} + mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ lookup.table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] + mstore({{ (32 * (offset + 2 + lookup.table_lines.len()))|hex_padded(4) }}, {{ (32 * lookup.inputs.len())|hex_padded(64) }}) // outer_inputs_len[{{ loop.index0 }}] {%- for input in lookup.inputs %} {%- let offset = offset + loop.index0 + input.acc + 3 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * input.expression.len())|hex_padded(64) }}) // inputs_len [{{ loop.index0 }}] {%- for expression in input.expression %} mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] {%- endfor %} From 1f69bbb10e12663fafd9e79e8f844a979c7ec27e Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 28 Jul 2024 21:45:33 -0500 Subject: [PATCH 36/65] set input_expressions_fsm_usage to 0x0 on None unwrap. --- src/codegen/evaluator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 1524cd1..4d2b80a 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -790,7 +790,7 @@ where *fsm_usages.iter().max().unwrap() }) .collect_vec(); - let input_expressions_fsm_usage = *input_expressions_fsm_usage.iter().max().unwrap(); + let input_expressions_fsm_usage = *input_expressions_fsm_usage.iter().max().unwrap_or(0x0); itertools::max([ gate_computation_fsm_usage, From a3ff8d527afd5dec9ccb28b067f9f20b85a18f32 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 29 Jul 2024 06:17:36 -0500 Subject: [PATCH 37/65] *add reference to 0x0 unwrap or. --- src/codegen/evaluator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 4d2b80a..2ec1ddb 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -790,7 +790,7 @@ where *fsm_usages.iter().max().unwrap() }) .collect_vec(); - let input_expressions_fsm_usage = *input_expressions_fsm_usage.iter().max().unwrap_or(0x0); + let input_expressions_fsm_usage = *input_expressions_fsm_usage.iter().max().unwrap_or(&0x0); itertools::max([ gate_computation_fsm_usage, From 2095a707ddb47b11d377b9043a06bc6e1dea69a2 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 29 Jul 2024 14:01:51 -0500 Subject: [PATCH 38/65] *fix bug with no lookup circuits. --- src/codegen/evaluator.rs | 6 +- templates/Halo2VerifierReusable.sol | 151 ++++++++++++++-------------- 2 files changed, 82 insertions(+), 75 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 2ec1ddb..024d8b2 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -644,7 +644,7 @@ impl Default for LookupsDataEncoded { impl LookupsDataEncoded { pub fn len(&self) -> usize { if self == &Self::default() { - 0 + 1 } else { 1 + self.lookups.iter().map(LookupEncoded::len).sum::() } @@ -896,6 +896,10 @@ where lookups, end_ptr: U256::from(0), }; + if data.lookups.is_empty() { + data.end_ptr = U256::from(0x0); + return data; + } data.end_ptr = U256::from((data.len() * 32) + offset); data } diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 66a3222..d100b38 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -649,88 +649,91 @@ contract Halo2Verifier { mstore(0x80, mload(add(theta_mptr, 0x20))) // beta let evals_ptr, end_ptr := soa_layout_metadata(0x3c0, vk_mptr) // TODO: Compute the end ptr of the lookup computations space // iterate through the input_tables_len - for { } lt(evals_ptr, end_ptr) { } { - let evals := mload(evals_ptr) - let phi := and(evals, 0xFFFF) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod(mload(0x20),calldataload(phi), R), - R - ) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod(mload(0x00), calldataload(phi), R), - R - ) - let table - // load in the table_lines_len from the evals_ptr - evals_ptr := add(evals_ptr, 0x20) - evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - let outer_inputs_len := mload(evals_ptr) - for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { + if end_ptr { + // iterate through the input_tables_len + for { } lt(evals_ptr, end_ptr) { } { + let evals := mload(evals_ptr) + let phi := and(evals, 0xFFFF) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x20),calldataload(phi), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x00), calldataload(phi), R), + R + ) + let table + // load in the table_lines_len from the evals_ptr evals_ptr := add(evals_ptr, 0x20) - // call the expression_evals function to evaluate the input_lines - let ident - evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) - // store ident in free static memory - mstore(j, ident) - } - evals_ptr := add(evals_ptr, 0x20) - let lhs - let rhs - switch eq(outer_inputs_len, 0x20) - case 1 { - rhs := table - } default { - // iterate through the outer_inputs_len - let last_idx := sub(outer_inputs_len, 0x20) - for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { + evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) + evals_ptr := add(evals_ptr, 0x20) + let outer_inputs_len := mload(evals_ptr) + for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { + evals_ptr := add(evals_ptr, 0x20) + // call the expression_evals function to evaluate the input_lines + let ident + evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) + // store ident in free static memory + mstore(j, ident) + } + evals_ptr := add(evals_ptr, 0x20) + let lhs + let rhs + switch eq(outer_inputs_len, 0x20) + case 1 { + rhs := table + } default { // iterate through the outer_inputs_len - let tmp := mload(0xa0) - if eq(i, 0){ - tmp := mload(0xc0) - } - for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { - if eq(i, j) { - continue + let last_idx := sub(outer_inputs_len, 0x20) + for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { + // iterate through the outer_inputs_len + let tmp := mload(0xa0) + if eq(i, 0){ + tmp := mload(0xc0) + } + for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + if eq(i, j) { + continue + } + tmp := mulmod(tmp, mload(j), R) + } - tmp := mulmod(tmp, mload(j), R) - + rhs := addmod(rhs, tmp, R) + if eq(i, last_idx) { + rhs := mulmod(rhs, table, R) + } } - rhs := addmod(rhs, tmp, R) - if eq(i, last_idx) { - rhs := mulmod(rhs, table, R) - } } - } - let tmp := mload(0xa0) - for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { - tmp := mulmod(tmp, mload(j), R) - } - rhs := addmod( - rhs, - sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), - R - ) - lhs := mulmod( - mulmod(table, tmp, R), - addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), - R - ) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod( - addmod( - 1, - sub(R, addmod(mload(0x40), mload(0x00), R)), + let tmp := mload(0xa0) + for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + tmp := mulmod(tmp, mload(j), R) + } + rhs := addmod( + rhs, + sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), + R + ) + lhs := mulmod( + mulmod(table, tmp, R), + addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + addmod( + 1, + sub(R, addmod(mload(0x40), mload(0x00), R)), + R + ), + addmod(lhs, sub(R, rhs), R), R ), - addmod(lhs, sub(R, rhs), R), R - ), - R - ) + ) + } } } From 8e21bfff956800eca67685a9caeb1a09656106d2 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 29 Jul 2024 18:14:48 -0500 Subject: [PATCH 39/65] *pcs point_computations --- src/codegen.rs | 18 +- src/codegen/pcs.rs | 516 ++++++++++++++++++++++++++++ src/codegen/template.rs | 7 +- src/codegen/util.rs | 3 +- src/test.rs | 6 +- templates/Halo2VerifierReusable.sol | 80 +++-- templates/Halo2VerifyingKey.sol | 6 +- 7 files changed, 598 insertions(+), 38 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 0c6c2f3..3a0ce4c 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -16,6 +16,7 @@ use halo2_proofs::{ poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG, Rotation}, }; use itertools::{chain, Itertools}; +use pcs::bdfg21_computations_separate; use ruint::aliases::U256; use std::{ collections::HashMap, @@ -204,6 +205,7 @@ impl<'a> SolidityGenerator<'a> { ("gate_computations_len_offset", U256::from(0)), ("permutation_computations_len_offset", U256::from(0)), ("lookup_computations_len_offset", U256::from(0)), + ("pcs_computations_len_offset", U256::from(0)), ("num_evals", U256::from(0)), ("num_neg_lagranges", U256::from(0)), ] @@ -322,6 +324,7 @@ impl<'a> SolidityGenerator<'a> { gate_computations: GateDataEncoded::default(), permutation_computations: PermutationDataEncoded::default(), lookup_computations: LookupsDataEncoded::default(), + pcs_computations: pcs::PcsDataEncoded::default(), }; if !separate { @@ -370,10 +373,15 @@ impl<'a> SolidityGenerator<'a> { vk_lookup_const_table_dummy, ); - // Fill in the gate computations with dummy values. (maintains the correct shape) + // Fill in the quotient eval computations with dummy values. (maintains the correct shape) let gate_computations_dummy = evaluator_dummy.gate_computations(); let permutation_computations_dummy = evaluator_dummy.permutation_computations(); let lookup_computations_dummy = evaluator_dummy.lookup_computations(0); + // Same for the pcs computations + let pcs_computations_dummy = match self.scheme { + Bdfg21 => bdfg21_computations_separate(&self.meta, &dummy_data), + Gwc19 => unimplemented!(), + }; let num_advices = self.meta.num_advices(); let num_user_challenges = self.meta.num_challenges(); @@ -406,6 +414,8 @@ impl<'a> SolidityGenerator<'a> { gate_computations_len_offset + (0x20 * gate_computations_dummy.len()); let lookup_computations_len_offset = permutations_computations_len_offset + (0x20 * permutation_computations_dummy.len()); + let pcs_computations_len_offset = + lookup_computations_len_offset + (0x20 * lookup_computations_dummy.len()); set_constant_value(&mut constants, "instance_cptr", instance_cptr); set_constant_value( @@ -438,6 +448,11 @@ impl<'a> SolidityGenerator<'a> { "lookup_computations_len_offset", U256::from(lookup_computations_len_offset), ); + set_constant_value( + &mut constants, + "pcs_computations_len_offset", + U256::from(pcs_computations_len_offset), + ); // Recreate the vk with the correct shape let mut vk = Halo2VerifyingKey { @@ -449,6 +464,7 @@ impl<'a> SolidityGenerator<'a> { gate_computations: gate_computations_dummy, permutation_computations: permutation_computations_dummy, lookup_computations: lookup_computations_dummy, + pcs_computations: pcs_computations_dummy, }; // Now generate the real vk_mptr with a vk that has the correct length diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index c038fe5..bf6a72b 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -4,6 +4,7 @@ use crate::codegen::util::{ for_loop, get_memory_ptr, ConstraintSystemMeta, Data, EcPoint, Location, Ptr, Word, }; use itertools::{chain, izip, Itertools}; +use ruint::aliases::U256; use std::collections::{BTreeMap, BTreeSet}; /// KZG batch open schemes in `halo2`. @@ -300,6 +301,8 @@ pub(crate) fn bdfg21_computations( }) ] .collect_vec(); + // print the point computations + // println!("{:?}", point_computations); let mu = get_memory_ptr(theta_mptr, 7, &separate); let vanishing_computations = chain![ [format!("let mu := mload({mu})").to_string()], @@ -660,6 +663,12 @@ pub(crate) fn bdfg21_computations( ] .collect_vec(); + let point_computations = if separate { + Vec::new() + } else { + point_computations + }; + chain![ [point_computations, vanishing_computations], coeff_computations, @@ -670,3 +679,510 @@ pub(crate) fn bdfg21_computations( ] .collect_vec() } + +// Holds the encoded data stored in the separate VK +// needed to perform the pcs computations portion of the reusable verifier. +#[derive(Clone, PartialEq, Eq, Default)] +pub struct PcsDataEncoded { + pub(crate) point_computations: Vec, +} + +// implement length of PcsDataEncoded +impl PcsDataEncoded { + pub fn len(&self) -> usize { + self.point_computations.len() + } +} + +pub(crate) fn bdfg21_computations_separate( + meta: &ConstraintSystemMeta, + data: &Data, +) -> PcsDataEncoded { + let queries = queries(meta, data); + let (superset, sets) = rotation_sets(&queries); + let min_rot = *superset.first().unwrap(); + let max_rot = *superset.last().unwrap(); + let num_coeffs = sets.iter().map(|set| set.rots().len()).sum::(); + + // let w = EcPoint::from(data.w_cptr); + // let w_prime = EcPoint::from(data.w_cptr + 2); + + let diff_0 = Word::from(Ptr::memory(0x00)); + // let coeffs = sets + // .iter() + // .scan(diff_0.ptr() + 1, |state, set| { + // let ptrs = Word::range(*state).take(set.rots().len()).collect_vec(); + // *state = *state + set.rots().len(); + // Some(ptrs) + // }) + // .collect_vec(); + + // let first_batch_invert_end = diff_0.ptr() + 1 + num_coeffs; + // let second_batch_invert_end = diff_0.ptr() + sets.len(); + let free_mptr = diff_0.ptr() + 2 * (1 + num_coeffs) + 6; + + let point_mptr = free_mptr; + // let mu_minus_point_mptr = point_mptr + superset.len(); + // let vanishing_0_mptr = mu_minus_point_mptr + superset.len(); + // let diff_mptr = vanishing_0_mptr + 1; + // let r_eval_mptr = diff_mptr + sets.len(); + // let sum_mptr = r_eval_mptr + sets.len(); + + // let point_vars = + // izip!(&superset, (0..).map(|idx| format!("point_{idx}"))).collect::>(); + let points = izip!(&superset, Word::range(point_mptr)).collect::>(); + // let mu_minus_points = + // izip!(&superset, Word::range(mu_minus_point_mptr)).collect::>(); + // let vanishing_0 = Word::from(vanishing_0_mptr); + // let diffs = Word::range(diff_mptr).take(sets.len()).collect_vec(); + // let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); + // let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); + // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. + // Otherwise we load in omega and omega_inv from the solidity constants. + + // let vk_mptr = "vk_mptr"; + // let theta_mptr = "theta_mptr"; + + // let omega = get_memory_ptr(vk_mptr, 10, &separate); + + // let omega_inv = get_memory_ptr(vk_mptr, 11, &separate); + + // let g1_x = get_memory_ptr(vk_mptr, 17, &separate); + + // let g1_y = get_memory_ptr(vk_mptr, 18, &separate); + + // let x = get_memory_ptr(theta_mptr, 4, &separate); + + // let zeta = get_memory_ptr(theta_mptr, 5, &separate); + let max_rot_computations: Vec = { + let pack_words = |points: Vec, interm_point: Option| { + let mut packed_words: Vec = vec![U256::from(0)]; + let mut bit_counter = 32; + let points_len = points.len(); + if let Some(interm_point) = interm_point { + packed_words[0] |= interm_point; + packed_words[0] |= U256::from(points_len) << 16; + bit_counter = 48; + } else { + packed_words[0] |= U256::from(points_len); + } + let mut last_idx = 0; + + for point in points.iter() { + let offset = 16; + + let mut next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + next_bit_counter = offset; + packed_words[last_idx] = *point + } else { + packed_words[last_idx] |= *point << bit_counter; + } + bit_counter = next_bit_counter; + } + + packed_words + }; + let max_rot_computations = (1..=max_rot) + .map(|rot| { + points + .get(&rot) + .map(|point| U256::from(point.ptr().value().as_usize())) + .unwrap_or(U256::from(0)) + }) + .collect_vec(); + let min_rot_computations = (min_rot..0) + .rev() + .map(|rot| { + points + .get(&rot) + .map(|point| U256::from(point.ptr().value().as_usize())) + .unwrap_or(U256::from(0)) + }) + .collect_vec(); + chain!( + pack_words(max_rot_computations, None).into_iter(), + pack_words( + min_rot_computations, + Some(U256::from(points[&0].ptr().value().as_usize())), + ) + .into_iter() + ) + .collect_vec() + }; + PcsDataEncoded { + point_computations: max_rot_computations, + } + + // let mu = get_memory_ptr(theta_mptr, 7, &separate); + // let vanishing_computations = chain![ + // [format!("let mu := mload({mu})").to_string()], + // { + // let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); + // let mptr_end = mptr + mu_minus_points.len(); + // for_loop( + // [ + // format!("let mptr := {mptr}"), + // format!("let mptr_end := {mptr_end}"), + // format!("let point_mptr := {free_mptr}"), + // ], + // "lt(mptr, mptr_end)", + // [ + // "mptr := add(mptr, 0x20)", + // "point_mptr := add(point_mptr, 0x20)", + // ] + // .map(str::to_string), + // ["mstore(mptr, addmod(mu, sub(R, mload(point_mptr)), R))".to_string()], + // ) + // }, + // ["let s".to_string()], + // chain![ + // [format!( + // "s := {}", + // mu_minus_points[sets[0].rots().first().unwrap()] + // )], + // chain![sets[0].rots().iter().skip(1)] + // .map(|rot| { format!("s := mulmod(s, {}, R)", mu_minus_points[rot]) }), + // [format!("mstore({}, s)", vanishing_0.ptr())], + // ], + // ["let diff".to_string()], + // izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, diff)| { + // chain![ + // [set.diffs() + // .first() + // .map(|rot| format!("diff := {}", mu_minus_points[rot])) + // .unwrap_or_else(|| "diff := 1".to_string())], + // chain![set.diffs().iter().skip(1)] + // .map(|rot| { format!("diff := mulmod(diff, {}, R)", mu_minus_points[rot]) }), + // [format!("mstore({}, diff)", diff.ptr())], + // (set_idx == 0).then(|| format!("mstore({}, diff)", diff_0.ptr())), + // ] + // }) + // ] + // .collect_vec(); + + // let coeff_computations = izip!(&sets, &coeffs) + // .map(|(set, coeffs)| { + // let coeff_points = set + // .rots() + // .iter() + // .map(|rot| &point_vars[rot]) + // .enumerate() + // .map(|(i, rot_i)| { + // set.rots() + // .iter() + // .map(|rot| &point_vars[rot]) + // .enumerate() + // .filter_map(|(j, rot_j)| (i != j).then_some((rot_i, rot_j))) + // .collect_vec() + // }) + // .collect_vec(); + // chain![ + // set.rots() + // .iter() + // .map(|rot| { format!("let {} := {}", &point_vars[rot], points[rot]) }), + // ["let coeff".to_string()], + // izip!(set.rots(), &coeff_points, coeffs).flat_map( + // |(rot_i, coeff_points, coeff)| chain![ + // [coeff_points + // .first() + // .map(|(point_i, point_j)| { + // format!("coeff := addmod({point_i}, sub(R, {point_j}), R)") + // }) + // .unwrap_or_else(|| { "coeff := 1".to_string() })], + // coeff_points.iter().skip(1).map(|(point_i, point_j)| { + // let item = format!("addmod({point_i}, sub(R, {point_j}), R)"); + // format!("coeff := mulmod(coeff, {item}, R)") + // }), + // [ + // format!("coeff := mulmod(coeff, {}, R)", mu_minus_points[rot_i]), + // format!("mstore({}, coeff)", coeff.ptr()) + // ], + // ] + // ) + // ] + // .collect_vec() + // }) + // .collect_vec(); + + // let normalized_coeff_computations = chain![ + // [ + // format!("success := batch_invert(success, 0, {first_batch_invert_end})"), + // format!("let diff_0_inv := {diff_0}"), + // format!("mstore({}, diff_0_inv)", diffs[0].ptr()), + // ], + // for_loop( + // [ + // format!("let mptr := {}", diffs[0].ptr() + 1), + // format!("let mptr_end := {}", diffs[0].ptr() + sets.len()), + // ], + // "lt(mptr, mptr_end)", + // ["mptr := add(mptr, 0x20)".to_string()], + // ["mstore(mptr, mulmod(mload(mptr), diff_0_inv, R))".to_string()], + // ), + // ] + // .collect_vec(); + + // let r_evals_computations = izip!(0.., &sets, &coeffs, &diffs, &r_evals).map( + // |(set_idx, set, coeffs, set_coeff, r_eval)| { + // let is_single_rot_set = set.rots().len() == 1; + // chain![ + // is_single_rot_set.then(|| format!("let coeff := {}", coeffs[0])), + // [ + // format!("let zeta := mload({})", zeta).as_str(), + // "let r_eval := 0" + // ] + // .map(str::to_string), + // if is_single_rot_set { + // let eval_groups = set.evals().iter().rev().fold( + // Vec::>::new(), + // |mut eval_groups, evals| { + // let eval = &evals[0]; + // if let Some(last_group) = eval_groups.last_mut() { + // let last_eval = **last_group.last().unwrap(); + // if last_eval.ptr().value().is_integer() + // && last_eval.ptr() - 1 == eval.ptr() + // { + // last_group.push(eval) + // } else { + // eval_groups.push(vec![eval]) + // } + // eval_groups + // } else { + // vec![vec![eval]] + // } + // }, + // ); + // chain![eval_groups.iter().enumerate()] + // .flat_map(|(group_idx, evals)| { + // if evals.len() < 3 { + // chain![evals.iter().enumerate()] + // .flat_map(|(eval_idx, eval)| { + // let is_first_eval = group_idx == 0 && eval_idx == 0; + // let item = format!("mulmod(coeff, {eval}, R)"); + // chain![ + // (!is_first_eval).then(|| format!( + // "r_eval := mulmod(r_eval, zeta, R)" + // )), + // [format!("r_eval := addmod(r_eval, {item}, R)")], + // ] + // }) + // .collect_vec() + // } else { + // let item = "mulmod(coeff, calldataload(mptr), R)"; + // for_loop( + // [ + // format!("let mptr := {}", evals[0].ptr()), + // format!("let mptr_end := {}", evals[0].ptr() - evals.len()), + // ], + // "lt(mptr_end, mptr)".to_string(), + // ["mptr := sub(mptr, 0x20)".to_string()], + // [format!( + // "r_eval := addmod(mulmod(r_eval, zeta, R), {item}, R)" + // )], + // ) + // } + // }) + // .collect_vec() + // } else { + // chain![set.evals().iter().enumerate().rev()] + // .flat_map(|(idx, evals)| { + // chain![ + // izip!(evals, coeffs).map(|(eval, coeff)| { + // let item = format!("mulmod({coeff}, {eval}, R)"); + // format!("r_eval := addmod(r_eval, {item}, R)") + // }), + // (idx != 0).then(|| format!("r_eval := mulmod(r_eval, zeta, R)")), + // ] + // }) + // .collect_vec() + // }, + // (set_idx != 0).then(|| format!("r_eval := mulmod(r_eval, {set_coeff}, R)")), + // [format!("mstore({}, r_eval)", r_eval.ptr())], + // ] + // .collect_vec() + // }, + // ); + + // let coeff_sums_computation = izip!(&coeffs, &sums).map(|(coeffs, sum)| { + // let (coeff_0, rest_coeffs) = coeffs.split_first().unwrap(); + // chain![ + // [format!("let sum := {coeff_0}")], + // rest_coeffs + // .iter() + // .map(|coeff_mptr| format!("sum := addmod(sum, {coeff_mptr}, R)")), + // [format!("mstore({}, sum)", sum.ptr())], + // ] + // .collect_vec() + // }); + + // let nu = get_memory_ptr(theta_mptr, 6, &separate); + // let mu = get_memory_ptr(theta_mptr, 7, &separate); + // let r_eval = get_memory_ptr(theta_mptr, 21, &separate); + // let pairing_lhs_x = get_memory_ptr(theta_mptr, 22, &separate); + // let pairing_lhs_y = get_memory_ptr(theta_mptr, 23, &separate); + // let pairing_rhs_x = get_memory_ptr(theta_mptr, 24, &separate); + // let pairing_rhs_y = get_memory_ptr(theta_mptr, 25, &separate); + // let r_eval_computations = chain![ + // for_loop( + // [ + // format!("let mptr := 0x00"), + // format!("let mptr_end := {second_batch_invert_end}"), + // format!("let sum_mptr := {}", sums[0].ptr()), + // ], + // "lt(mptr, mptr_end)", + // ["mptr := add(mptr, 0x20)", "sum_mptr := add(sum_mptr, 0x20)"].map(str::to_string), + // ["mstore(mptr, mload(sum_mptr))".to_string()], + // ), + // [ + // format!("success := batch_invert(success, 0, {second_batch_invert_end})"), + // format!( + // "let r_eval := mulmod(mload({}), {}, R)", + // second_batch_invert_end - 1, + // r_evals.last().unwrap() + // ) + // ], + // for_loop( + // [ + // format!("let sum_inv_mptr := {}", second_batch_invert_end - 2), + // format!("let sum_inv_mptr_end := {second_batch_invert_end}"), + // format!("let r_eval_mptr := {}", r_evals[r_evals.len() - 2].ptr()), + // ], + // "lt(sum_inv_mptr, sum_inv_mptr_end)", + // [ + // "sum_inv_mptr := sub(sum_inv_mptr, 0x20)", + // "r_eval_mptr := sub(r_eval_mptr, 0x20)" + // ] + // .map(str::to_string), + // [ + // format!("r_eval := mulmod(r_eval, mload({nu}), R)").as_str(), + // "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), R), R)" + // ] + // .map(str::to_string), + // ), + // [format!("mstore({r_eval}, r_eval)")], + // ] + // .collect_vec(); + + // let pairing_input_computations = chain![ + // [format!("let nu := mload({nu})").to_string()], + // izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, set_coeff)| { + // let is_first_set = set_idx == 0; + // let is_last_set = set_idx == sets.len() - 1; + + // let ec_add = &format!("ec_add_{}", if is_first_set { "acc" } else { "tmp" }); + // let ec_mul = &format!("ec_mul_{}", if is_first_set { "acc" } else { "tmp" }); + // let acc_x = Ptr::memory(0x00) + if is_first_set { 0 } else { 4 }; + // let acc_y = acc_x + 1; + + // let comm_groups = set.comms().iter().rev().skip(1).fold( + // Vec::<(Location, Vec<&EcPoint>)>::new(), + // |mut comm_groups, comm| { + // if let Some(last_group) = comm_groups.last_mut() { + // let last_comm = **last_group.1.last().unwrap(); + // if last_group.0 == comm.loc() + // && last_comm.x().ptr().value().is_integer() + // && last_comm.x().ptr() - 2 == comm.x().ptr() + // { + // last_group.1.push(comm) + // } else { + // comm_groups.push((comm.loc(), vec![comm])) + // } + // comm_groups + // } else { + // vec![(comm.loc(), vec![comm])] + // } + // }, + // ); + // let zeta_mptr = ζ + // chain![ + // set.comms() + // .last() + // .map(|comm| { + // [ + // format!("mstore({acc_x}, {})", comm.x()), + // format!("mstore({acc_y}, {})", comm.y()), + // ] + // }) + // .into_iter() + // .flatten(), + // comm_groups.into_iter().flat_map(move |(loc, comms)| { + // if comms.len() < 3 { + // comms + // .iter() + // .flat_map(|comm| { + // let (x, y) = (comm.x(), comm.y()); + // [ + // format!("success := {ec_mul}(success, mload({zeta_mptr}))"), + // format!("success := {ec_add}(success, {x}, {y})"), + // ] + // }) + // .collect_vec() + // } else { + // let mptr = comms.first().unwrap().x().ptr(); + // let mptr_end = mptr - 2 * comms.len(); + // let x = Word::from(Ptr::new(loc, "mptr")); + // let y = Word::from(Ptr::new(loc, "add(mptr, 0x20)")); + // for_loop( + // [ + // format!("let mptr := {mptr}"), + // format!("let mptr_end := {mptr_end}"), + // ], + // "lt(mptr_end, mptr)", + // ["mptr := sub(mptr, 0x40)".to_string()], + // [ + // format!("success := {ec_mul}(success, mload({zeta_mptr}))"), + // format!("success := {ec_add}(success, {x}, {y})"), + // ], + // ) + // } + // }), + // (!is_first_set) + // .then(|| { + // let scalar = format!("mulmod(nu, {set_coeff}, R)"); + // chain![ + // [ + // format!("success := ec_mul_tmp(success, {scalar})"), + // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), + // ], + // (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), R)")) + // ] + // }) + // .into_iter() + // .flatten(), + // ] + // .collect_vec() + // }), + // [ + // format!("mstore(0x80, mload({}))", g1_x), + // format!("mstore(0xa0, mload({}))", g1_y), + // format!("success := ec_mul_tmp(success, sub(R, mload({r_eval})))"), + // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), + // format!("mstore(0x80, {})", w.x()), + // format!("mstore(0xa0, {})", w.y()), + // format!("success := ec_mul_tmp(success, sub(R, {vanishing_0}))"), + // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), + // format!("mstore(0x80, {})", w_prime.x()), + // format!("mstore(0xa0, {})", w_prime.y()), + // format!("success := ec_mul_tmp(success, mload({mu}))"), + // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), + // format!("mstore({pairing_lhs_x}, mload(0x00))"), + // format!("mstore({pairing_lhs_y}, mload(0x20))"), + // format!("mstore({pairing_rhs_x}, {})", w_prime.x()), + // format!("mstore({pairing_rhs_y}, {})", w_prime.y()), + // ], + // ] + // .collect_vec(); + + // chain![ + // [point_computations, vanishing_computations], + // coeff_computations, + // [normalized_coeff_computations], + // r_evals_computations, + // coeff_sums_computation, + // [r_eval_computations, pairing_input_computations], + // ] + // .collect_vec() +} diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 525a786..de13d62 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -7,7 +7,10 @@ use ruint::aliases::U256; use std::collections::HashMap; use std::fmt; -use super::evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}; +use super::{ + evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}, + pcs::PcsDataEncoded, +}; #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] @@ -20,6 +23,7 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) gate_computations: GateDataEncoded, pub(crate) permutation_computations: PermutationDataEncoded, pub(crate) lookup_computations: LookupsDataEncoded, + pub(crate) pcs_computations: PcsDataEncoded, } impl Halo2VerifyingKey { @@ -31,6 +35,7 @@ impl Halo2VerifyingKey { + (self.gate_computations.len() * 0x20) + (self.permutation_computations.len() * 0x20) + (self.lookup_computations.len() * 0x20) + + (self.pcs_computations.len() * 0x20) } } diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 15853ea..3bab77b 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -404,7 +404,8 @@ impl Data { + (2 * vk.num_advices_user_challenges.len() + 1) + (vk.gate_computations.len()) + (vk.permutation_computations.len()) - + (vk.lookup_computations.len()); + + (vk.lookup_computations.len()) + + (vk.pcs_computations.len()); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; diff --git a/src/test.rs b/src/test.rs index 32cb358..5326f0c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -97,10 +97,10 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); - // print verifier_solidity + // // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); - // print vk_solidity - println!("VK solidity: {vk_solidity}"); + // // print vk_solidity + // println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); let vk_address = evm.create(vk_creation_code); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index d100b38..f6a26a9 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -306,37 +306,31 @@ contract Halo2Verifier { } ret1 := addmod(ret1, beta, R) } - - function expression_evals(fsmp, code_len, code_ptr) { - for { let i := 0 } lt(i, code_len) { i := add(i, 0x20) } { - /// @dev Note we can optimize the amount of space the expressions take up by packing 32/5 == 6 expressions into a single word - let expression := mload(add(code_ptr, i)) - // Load in the least significant byte of the `expression` word to get the operation type - // Then determine which operation to peform and then store the result in the next available memory slot. - switch and(expression, 0xFF) - // 0x00 => Advice/Fixed expression - case 0x00 { - // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - mstore(add(fsmp, i), calldataload(and(shr(8, expression), 0xFFFF))) - } - // 0x01 => Negated expression - case 0x01 { - // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - mstore(add(fsmp, i), sub(R, mload(and(shr(8, expression), 0xFFFF)))) + + function point_rots(pcs_computations, pcs_ptr, word_shift, x_pow_of_omega, omega) -> ret0, ret1 { + // Extract the 32 LSG bits (4 bytes) from the pcs_computations word to get the max rot + let values_max_rot := and(pcs_computations, 0xFFFFFFFF) + pcs_computations := shr(32, pcs_computations) + for { let i := 0 } lt(i, values_max_rot) { i := add(i, 1) } { + let value := and(pcs_computations, 0xFFFF) + if not(eq(value, 0)) { + mstore(value, x_pow_of_omega) } - // 0x02 => Sum expression - case 0x02 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(add(fsmp, i), addmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + if eq(i, sub(values_max_rot, 1)) { + break } - // 0x03 => Product/scalar expression - case 0x03 { - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(add(fsmp, i), mulmod(mload(and(shr(8, expression), 0xFFFF)),mload(and(shr(24, expression), 0xFFFF)),R)) + x_pow_of_omega := mulmod(x_pow_of_omega, omega, R) + word_shift := add(word_shift, 16) + pcs_computations := shr(16, pcs_computations) + // We can pack up to + if eq(word_shift, 256) { + word_shift := 0 + pcs_ptr := add(pcs_ptr, 0x20) + pcs_computations := mload(pcs_ptr) } } + ret0 := x_pow_of_omega + ret1 := pcs_ptr } // Modulus @@ -568,10 +562,6 @@ contract Halo2Verifier { // Compute quotient evavluation - // TODO: - // [X] Gate computations - // [X] Permutation computations - // [X] Lookup computations { let quotient_eval_numer let y := mload(add(theta_mptr, 0x60)) @@ -765,7 +755,35 @@ contract Halo2Verifier { } // Compute pairing lhs and rhs + // TODO: + // [X] point_computations + // [] vanishing_computation + // [] coeff_computations + // [] formalized_coeff_computations + // [] r_evals_computations + // [] coeff_sums_computation + // [] r_eval_computations + // [] pairing_input_computations { + // point_computations + let pcs_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["pcs_computations_len_offset"]|hex() }}))) + let pcs_computations := mload(pcs_ptr) + { + let x := mload(add(theta_mptr, 0x80)) + let omega := mload(add(vk_mptr, {{ vk_const_offsets["omega"]|hex() }})) + let omega_inv := mload(add(vk_mptr, {{ vk_const_offsets["omega_inv"]|hex() }})) + let x_pow_of_omega := mulmod(x, omega, R) + x_pow_of_omega, pcs_ptr := point_rots(pcs_computations, pcs_ptr, 32, x_pow_of_omega, omega) + pcs_ptr := add(pcs_ptr, 0x20) + pcs_computations := mload(pcs_ptr) + // Compute interm point + mstore(and(pcs_computations, 0xFFFF), x) + x_pow_of_omega := mulmod(x, omega_inv, R) + pcs_computations := shr(16, pcs_computations) + x_pow_of_omega, pcs_ptr := point_rots(pcs_computations, pcs_ptr, 48, x_pow_of_omega, omega_inv) + pcs_ptr := add(pcs_ptr, 0x20) + pop(x_pow_of_omega) + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 389b9af..23a8c05 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -77,7 +77,11 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() ))|hex() }}) + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() %} + {%- for point_word in pcs_computations.point_computations %} + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] + {%- endfor %} + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } } From 030051f67598cb7c515cb862ec1b7cfc1cd75f31 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 29 Jul 2024 20:32:17 -0500 Subject: [PATCH 40/65] *vanishing_computations --- src/codegen/pcs.rs | 93 +++++++++++++++++++++++++---- src/test.rs | 8 +-- templates/Halo2VerifierReusable.sol | 55 +++++++++++++++++ templates/Halo2VerifyingKey.sol | 4 ++ 4 files changed, 144 insertions(+), 16 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index bf6a72b..84daf36 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -668,6 +668,11 @@ pub(crate) fn bdfg21_computations( } else { point_computations }; + let vanishing_computations = if separate { + Vec::new() + } else { + vanishing_computations + }; chain![ [point_computations, vanishing_computations], @@ -685,12 +690,13 @@ pub(crate) fn bdfg21_computations( #[derive(Clone, PartialEq, Eq, Default)] pub struct PcsDataEncoded { pub(crate) point_computations: Vec, + pub(crate) vanishing_computations: Vec, } // implement length of PcsDataEncoded impl PcsDataEncoded { pub fn len(&self) -> usize { - self.point_computations.len() + self.point_computations.len() + self.vanishing_computations.len() } } @@ -722,19 +728,19 @@ pub(crate) fn bdfg21_computations_separate( let free_mptr = diff_0.ptr() + 2 * (1 + num_coeffs) + 6; let point_mptr = free_mptr; - // let mu_minus_point_mptr = point_mptr + superset.len(); - // let vanishing_0_mptr = mu_minus_point_mptr + superset.len(); - // let diff_mptr = vanishing_0_mptr + 1; + let mu_minus_point_mptr = point_mptr + superset.len(); + let vanishing_0_mptr = mu_minus_point_mptr + superset.len(); + let diff_mptr = vanishing_0_mptr + 1; // let r_eval_mptr = diff_mptr + sets.len(); // let sum_mptr = r_eval_mptr + sets.len(); // let point_vars = // izip!(&superset, (0..).map(|idx| format!("point_{idx}"))).collect::>(); let points = izip!(&superset, Word::range(point_mptr)).collect::>(); - // let mu_minus_points = - // izip!(&superset, Word::range(mu_minus_point_mptr)).collect::>(); - // let vanishing_0 = Word::from(vanishing_0_mptr); - // let diffs = Word::range(diff_mptr).take(sets.len()).collect_vec(); + let mu_minus_points = + izip!(&superset, Word::range(mu_minus_point_mptr)).collect::>(); + let vanishing_0 = Word::from(vanishing_0_mptr); + let diffs = Word::range(diff_mptr).take(sets.len()).collect_vec(); // let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); // let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. @@ -754,7 +760,7 @@ pub(crate) fn bdfg21_computations_separate( // let x = get_memory_ptr(theta_mptr, 4, &separate); // let zeta = get_memory_ptr(theta_mptr, 5, &separate); - let max_rot_computations: Vec = { + let point_computations: Vec = { let pack_words = |points: Vec, interm_point: Option| { let mut packed_words: Vec = vec![U256::from(0)]; let mut bit_counter = 32; @@ -812,11 +818,74 @@ pub(crate) fn bdfg21_computations_separate( ) .collect_vec() }; + + let vanishing_computations: Vec = { + let pack_mptrs_and_s_ptrs = { + let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); + let mptr_word = U256::from(mptr.value().as_usize()); + let mptr_end = mptr + mu_minus_points.len(); + let mptr_end_word = U256::from(mptr_end.value().as_usize()); + let mut packed_word = U256::from(0); + // start packing the mptrs + packed_word |= mptr_word; + packed_word |= mptr_end_word << 16; + packed_word |= U256::from(free_mptr.value().as_usize()) << 32; + let mut offset = 48; + // start packing the s_ptrs + sets[0].rots().iter().for_each(|rot| { + // panic if offset is exceeds 256 + assert!(offset <= 256); + packed_word |= U256::from(mu_minus_points[rot].ptr().value().as_usize()) << offset; + offset += 16; + }); + packed_word + }; + let pack_vanishing_0_and_sets_len: ruint::Uint<256, 4> = { + let vanishing_0_word = U256::from(vanishing_0.ptr().value().as_usize()); + let sets_len = U256::from(sets.len()); + let mut packed_word = U256::from(0); + packed_word |= vanishing_0_word; + packed_word |= sets_len << 16; + packed_word + }; + let pack_set_diffs_words: Vec> = { + izip!(&sets, &diffs) + .map(|(set, diff)| { + let mut packed_word = U256::from(0); + let mut offset = 0; + set.diffs().iter().for_each(|rot| { + packed_word |= + U256::from(mu_minus_points[rot].ptr().value().as_usize()) << offset; + offset += 16; + }); + if set.diffs.is_empty() { + // 0x20 is where 1 is stored in memory in this block + packed_word |= U256::from(0x20) << offset; + offset += 16; + } + // pack a blank word + packed_word |= U256::from(0) << offset; + offset += 16; + // pack the diff.ptr() + packed_word |= U256::from(diff.ptr().value().as_usize()) << offset; + assert!( + offset <= 256, + "The offset for packing the set diff words exceeds 256", + ); + packed_word + }) + .collect_vec() + }; + chain!( + [pack_mptrs_and_s_ptrs, pack_vanishing_0_and_sets_len], + pack_set_diffs_words.into_iter() + ) + .collect_vec() + }; PcsDataEncoded { - point_computations: max_rot_computations, + point_computations, + vanishing_computations, } - - // let mu = get_memory_ptr(theta_mptr, 7, &separate); // let vanishing_computations = chain![ // [format!("let mu := mload({mu})").to_string()], // { diff --git a/src/test.rs b/src/test.rs index 5326f0c..9375723 100644 --- a/src/test.rs +++ b/src/test.rs @@ -97,10 +97,10 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); - // // print verifier_solidity - // println!("Verifier solidity: {verifier_solidity}"); - // // print vk_solidity - // println!("VK solidity: {vk_solidity}"); + // print verifier_solidity + println!("Verifier solidity: {verifier_solidity}"); + // print vk_solidity + println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); let vk_address = evm.create(vk_creation_code); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index f6a26a9..b177170 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -784,6 +784,61 @@ contract Halo2Verifier { pcs_ptr := add(pcs_ptr, 0x20) pop(x_pow_of_omega) } + + // vanishing_computations + { + let mu := mload(add(theta_mptr, 0xE0)) + pcs_computations := mload(pcs_ptr) + mstore(0x20, 1) + for + { + let mptr := and(pcs_computations, 0xFFFF) + pcs_computations := shr(16, pcs_computations) + let mptr_end := and(pcs_computations, 0xFFFF) + pcs_computations := shr(16, pcs_computations) + let point_mptr := and(pcs_computations, 0xFFFF) + } + lt(mptr, mptr_end) + { + mptr := add(mptr, 0x20) + point_mptr := add(point_mptr, 0x20) + } + { + mstore(mptr, addmod(mu, sub(R, mload(point_mptr)), R)) + } + pop(mu) + pcs_computations := shr(16, pcs_computations) + let s + s := mload(and(pcs_computations, 0xFFFF)) + pcs_computations := shr(16, pcs_computations) + for { } pcs_computations { } { + s := mulmod(s, mload(and(pcs_computations, 0xFFFF)), R) + pcs_computations := shr(16, pcs_computations) + } + pcs_ptr := add(pcs_ptr, 0x20) + pcs_computations := mload(pcs_ptr) + mstore(and(pcs_computations, 0xFFFF), s) + pcs_computations := shr(16, pcs_computations) + let diff + let sets_len := and(pcs_computations, 0xFFFF) + pcs_ptr := add(pcs_ptr, 0x20) + pcs_computations := mload(pcs_ptr) + for { let i := 0 } lt(i, sets_len) { i := add(i, 1) } { + diff := mload(and(pcs_computations, 0xFFFF)) + pcs_computations := shr(16, pcs_computations) + for { } and(pcs_computations, 0xFFFF) { } { + diff := mulmod(diff, mload(and(pcs_computations, 0xFFFF)), R) + pcs_computations := shr(16, pcs_computations) + } + pcs_computations := shr(16, pcs_computations) + mstore(and(pcs_computations, 0xFFFF), diff) + if eq(i, 0) { + mstore(0x00, diff) + } + pcs_ptr := add(pcs_ptr, 0x20) + pcs_computations := mload(pcs_ptr) + } + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 23a8c05..361d956 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -81,6 +81,10 @@ contract Halo2VerifyingKey { {%- for point_word in pcs_computations.point_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] {%- endfor %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() %} + {%- for vanishing_word in pcs_computations.vanishing_computations %} + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] + {%- endfor %} return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } From 9fe8835888e986e398b2795d6cc99e060cb8e417 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 30 Jul 2024 19:12:45 -0500 Subject: [PATCH 41/65] *coeff_computations. --- src/codegen/pcs.rs | 132 ++++++++++++++++------------ templates/Halo2VerifierReusable.sol | 61 +++++++++++-- templates/Halo2VerifyingKey.sol | 4 + 3 files changed, 137 insertions(+), 60 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 84daf36..bf114c4 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -673,6 +673,11 @@ pub(crate) fn bdfg21_computations( } else { vanishing_computations }; + let coeff_computations = if separate { + Vec::new() + } else { + coeff_computations + }; chain![ [point_computations, vanishing_computations], @@ -691,12 +696,15 @@ pub(crate) fn bdfg21_computations( pub struct PcsDataEncoded { pub(crate) point_computations: Vec, pub(crate) vanishing_computations: Vec, + pub(crate) coeff_computations: Vec, } // implement length of PcsDataEncoded impl PcsDataEncoded { pub fn len(&self) -> usize { - self.point_computations.len() + self.vanishing_computations.len() + self.point_computations.len() + + self.vanishing_computations.len() + + self.coeff_computations.len() } } @@ -714,14 +722,14 @@ pub(crate) fn bdfg21_computations_separate( // let w_prime = EcPoint::from(data.w_cptr + 2); let diff_0 = Word::from(Ptr::memory(0x00)); - // let coeffs = sets - // .iter() - // .scan(diff_0.ptr() + 1, |state, set| { - // let ptrs = Word::range(*state).take(set.rots().len()).collect_vec(); - // *state = *state + set.rots().len(); - // Some(ptrs) - // }) - // .collect_vec(); + let coeffs = sets + .iter() + .scan(diff_0.ptr() + 1, |state, set| { + let ptrs = Word::range(*state).take(set.rots().len()).collect_vec(); + *state = *state + set.rots().len(); + Some(ptrs) + }) + .collect_vec(); // let first_batch_invert_end = diff_0.ptr() + 1 + num_coeffs; // let second_batch_invert_end = diff_0.ptr() + sets.len(); @@ -870,7 +878,7 @@ pub(crate) fn bdfg21_computations_separate( packed_word |= U256::from(diff.ptr().value().as_usize()) << offset; assert!( offset <= 256, - "The offset for packing the set diff words exceeds 256", + "The offset for packing the set diff word exceeds 256 bits", ); packed_word }) @@ -882,55 +890,69 @@ pub(crate) fn bdfg21_computations_separate( ) .collect_vec() }; + let coeff_computations: Vec = { + // 1) The first LSG byte of the first word will contain the number of words that are used to store the + // the set.rots().len(). The rest of the bytes in the word will contain the set.rots().len() + let coeff_len_words: Vec = { + let mut packed_words: Vec = vec![U256::from(0)]; + let mut bit_counter = 8; + let mut last_idx = 0; + for set in sets.iter() { + let coeff_len = set.rots().len(); + assert!(coeff_len <= 5, "The number of rotations in a set exceeds 5 for the coeff_computations. Can't pack all the coef_data in a single word"); + let offset = 8; + let mut next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + next_bit_counter = offset; + packed_words[last_idx] = U256::from(coeff_len) + } else { + packed_words[last_idx] |= U256::from(coeff_len) << bit_counter; + } + bit_counter = next_bit_counter; + } + let packed_words_len = packed_words.len(); + // Encode the length of the exprs vec in the first word + packed_words[0] |= U256::from(packed_words_len); + packed_words + }; + // The next set of words will contain the points for the set.rots() followed by the mu_minus_points + // and the coeff.ptr(). Throw if set.rots().len > 5 b/c anything greater than 5 we can't pack all the ptr data into a single word. + let coeff_data_words: Vec = izip!(&sets, &coeffs) + .map(|(set, coeffs)| { + let mut packed_word = U256::from(0); + let mut offset = 0; + if set.rots().len() > 1 { + set.rots().iter().for_each(|rot| { + packed_word |= U256::from(points[rot].ptr().value().as_usize()) << offset; + offset += 16; + }); + } + set.rots().iter().for_each(|rot| { + packed_word |= + U256::from(mu_minus_points[rot].ptr().value().as_usize()) << offset; + offset += 16; + }); + coeffs.iter().for_each(|coeff| { + packed_word |= U256::from(coeff.ptr().value().as_usize()) << offset; + offset += 16; + }); + assert!( + offset <= 256, + "The offset for packing the coeff computation word exceeds 256 bits", + ); + packed_word + }) + .collect_vec(); + chain!(coeff_len_words.into_iter(), coeff_data_words.into_iter()).collect_vec() + }; + PcsDataEncoded { point_computations, vanishing_computations, + coeff_computations, } - // let vanishing_computations = chain![ - // [format!("let mu := mload({mu})").to_string()], - // { - // let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); - // let mptr_end = mptr + mu_minus_points.len(); - // for_loop( - // [ - // format!("let mptr := {mptr}"), - // format!("let mptr_end := {mptr_end}"), - // format!("let point_mptr := {free_mptr}"), - // ], - // "lt(mptr, mptr_end)", - // [ - // "mptr := add(mptr, 0x20)", - // "point_mptr := add(point_mptr, 0x20)", - // ] - // .map(str::to_string), - // ["mstore(mptr, addmod(mu, sub(R, mload(point_mptr)), R))".to_string()], - // ) - // }, - // ["let s".to_string()], - // chain![ - // [format!( - // "s := {}", - // mu_minus_points[sets[0].rots().first().unwrap()] - // )], - // chain![sets[0].rots().iter().skip(1)] - // .map(|rot| { format!("s := mulmod(s, {}, R)", mu_minus_points[rot]) }), - // [format!("mstore({}, s)", vanishing_0.ptr())], - // ], - // ["let diff".to_string()], - // izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, diff)| { - // chain![ - // [set.diffs() - // .first() - // .map(|rot| format!("diff := {}", mu_minus_points[rot])) - // .unwrap_or_else(|| "diff := 1".to_string())], - // chain![set.diffs().iter().skip(1)] - // .map(|rot| { format!("diff := mulmod(diff, {}, R)", mu_minus_points[rot]) }), - // [format!("mstore({}, diff)", diff.ptr())], - // (set_idx == 0).then(|| format!("mstore({}, diff)", diff_0.ptr())), - // ] - // }) - // ] - // .collect_vec(); // let coeff_computations = izip!(&sets, &coeffs) // .map(|(set, coeffs)| { diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index b177170..8e5a841 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -322,7 +322,6 @@ contract Halo2Verifier { x_pow_of_omega := mulmod(x_pow_of_omega, omega, R) word_shift := add(word_shift, 16) pcs_computations := shr(16, pcs_computations) - // We can pack up to if eq(word_shift, 256) { word_shift := 0 pcs_ptr := add(pcs_ptr, 0x20) @@ -333,6 +332,41 @@ contract Halo2Verifier { ret1 := pcs_ptr } + function coeff_computations(coeff_len_data, coeff_data) -> ret { + let coeff_len := and(coeff_len_data, 0xFF) + ret := shr(8, coeff_len_data) + switch coeff_len + case 0x01 { + // We only encode the points if the coeff length is greater than 1. + // Otherwise we just encode the mu_minus_point and coeff ptr. + mstore(and(shr(16, coeff_data), 0xFFFF), mod(mload(and(coeff_data, 0xFFFF)), R)) + } + default { + let coeff + let offset_aggr := mul(coeff_len, 16) + for { let i := 0 } lt(i, coeff_len) { i := add(i, 1) } { + let first := 0x01 + let offset_base := mul(i, 16) + let point_i := mload(and(shr(offset_base, coeff_data), 0xFFFF)) + for { let j:= 0 } lt(j, coeff_len) { j := add(j, 1) } { + if eq(j, i) { + continue + } + if first { + coeff := addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), 0xFFFF))), R) + first := 0 + continue + } + coeff := mulmod(coeff, addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), 0xFFFF))), R), R) + } + offset_base := add(offset_base, offset_aggr) + coeff := mulmod(coeff, mload(and(shr(offset_base, coeff_data), 0xFFFF)), R) + offset_base := add(offset_base, offset_aggr) + mstore(and(shr(offset_base, coeff_data), 0xFFFF), coeff) + } + } + } + // Modulus // Initialize success as true @@ -637,8 +671,7 @@ contract Halo2Verifier { mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind mstore(0x60, mload(theta_mptr)) // theta mstore(0x80, mload(add(theta_mptr, 0x20))) // beta - let evals_ptr, end_ptr := soa_layout_metadata(0x3c0, vk_mptr) // TODO: Compute the end ptr of the lookup computations space - // iterate through the input_tables_len + let evals_ptr, end_ptr := soa_layout_metadata(0x3c0, vk_mptr) if end_ptr { // iterate through the input_tables_len for { } lt(evals_ptr, end_ptr) { } { @@ -757,8 +790,8 @@ contract Halo2Verifier { // Compute pairing lhs and rhs // TODO: // [X] point_computations - // [] vanishing_computation - // [] coeff_computations + // [x] vanishing_computation + // [x] coeff_computations // [] formalized_coeff_computations // [] r_evals_computations // [] coeff_sums_computation @@ -839,6 +872,24 @@ contract Halo2Verifier { pcs_computations := mload(pcs_ptr) } } + + // coeff_computations + { + let coeff_len_data := mload(pcs_ptr) + // Load in the least significant byte of the `coeff_len_data` word to get the total number of words we will need to load in + // that contain the packed Vec. + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_len_data, 0xFF))) + coeff_len_data := shr(8, coeff_len_data) + let i := pcs_ptr + pcs_ptr := end_ptr_packed_lens + for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { + for { } coeff_len_data { } { + coeff_len_data := coeff_computations(coeff_len_data, mload(pcs_ptr)) + pcs_ptr := add(pcs_ptr, 0x20) + } + coeff_len_data := mload(add(i, 0x20)) + } + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 361d956..6930603 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -85,6 +85,10 @@ contract Halo2VerifyingKey { {%- for vanishing_word in pcs_computations.vanishing_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] {%- endfor %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() %} + {%- for coeff_word in pcs_computations.coeff_computations %} + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] + {%- endfor %} return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } From c4380e48562cfc49c8bce00763792f1c2a4c16e5 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 30 Jul 2024 19:47:00 -0500 Subject: [PATCH 42/65] *normalized pcs --- src/codegen/pcs.rs | 65 +++++++++-------------------- src/test.rs | 8 ++-- templates/Halo2VerifierReusable.sol | 21 ++++++++++ templates/Halo2VerifyingKey.sol | 2 + 4 files changed, 47 insertions(+), 49 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index bf114c4..ca1ba51 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -678,6 +678,11 @@ pub(crate) fn bdfg21_computations( } else { coeff_computations }; + let normalized_coeff_computations = if separate { + Vec::new() + } else { + normalized_coeff_computations + }; chain![ [point_computations, vanishing_computations], @@ -697,6 +702,7 @@ pub struct PcsDataEncoded { pub(crate) point_computations: Vec, pub(crate) vanishing_computations: Vec, pub(crate) coeff_computations: Vec, + pub(crate) normalized_coeff_computations: U256, } // implement length of PcsDataEncoded @@ -705,6 +711,7 @@ impl PcsDataEncoded { self.point_computations.len() + self.vanishing_computations.len() + self.coeff_computations.len() + + 1 // normalized_coeff_computations } } @@ -731,7 +738,7 @@ pub(crate) fn bdfg21_computations_separate( }) .collect_vec(); - // let first_batch_invert_end = diff_0.ptr() + 1 + num_coeffs; + let first_batch_invert_end = diff_0.ptr() + 1 + num_coeffs; // let second_batch_invert_end = diff_0.ptr() + sets.len(); let free_mptr = diff_0.ptr() + 2 * (1 + num_coeffs) + 6; @@ -948,56 +955,24 @@ pub(crate) fn bdfg21_computations_separate( chain!(coeff_len_words.into_iter(), coeff_data_words.into_iter()).collect_vec() }; + let normalized_coeff_computations: U256 = { + let mut packed_word = U256::from(0); + let mut offset = 0; + packed_word |= U256::from(first_batch_invert_end.value().as_usize()) << offset; + offset += 16; + packed_word |= U256::from(diffs[0].ptr().value().as_usize()) << offset; + offset += 16; + packed_word |= U256::from(sets.len() * 32) << offset; + packed_word + }; + PcsDataEncoded { point_computations, vanishing_computations, coeff_computations, + normalized_coeff_computations, } - // let coeff_computations = izip!(&sets, &coeffs) - // .map(|(set, coeffs)| { - // let coeff_points = set - // .rots() - // .iter() - // .map(|rot| &point_vars[rot]) - // .enumerate() - // .map(|(i, rot_i)| { - // set.rots() - // .iter() - // .map(|rot| &point_vars[rot]) - // .enumerate() - // .filter_map(|(j, rot_j)| (i != j).then_some((rot_i, rot_j))) - // .collect_vec() - // }) - // .collect_vec(); - // chain![ - // set.rots() - // .iter() - // .map(|rot| { format!("let {} := {}", &point_vars[rot], points[rot]) }), - // ["let coeff".to_string()], - // izip!(set.rots(), &coeff_points, coeffs).flat_map( - // |(rot_i, coeff_points, coeff)| chain![ - // [coeff_points - // .first() - // .map(|(point_i, point_j)| { - // format!("coeff := addmod({point_i}, sub(R, {point_j}), R)") - // }) - // .unwrap_or_else(|| { "coeff := 1".to_string() })], - // coeff_points.iter().skip(1).map(|(point_i, point_j)| { - // let item = format!("addmod({point_i}, sub(R, {point_j}), R)"); - // format!("coeff := mulmod(coeff, {item}, R)") - // }), - // [ - // format!("coeff := mulmod(coeff, {}, R)", mu_minus_points[rot_i]), - // format!("mstore({}, coeff)", coeff.ptr()) - // ], - // ] - // ) - // ] - // .collect_vec() - // }) - // .collect_vec(); - // let normalized_coeff_computations = chain![ // [ // format!("success := batch_invert(success, 0, {first_batch_invert_end})"), diff --git a/src/test.rs b/src/test.rs index 9375723..5326f0c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -97,10 +97,10 @@ fn run_render_separately>() { let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); assert_eq!(deployed_verifier_solidity, verifier_solidity); - // print verifier_solidity - println!("Verifier solidity: {verifier_solidity}"); - // print vk_solidity - println!("VK solidity: {vk_solidity}"); + // // print verifier_solidity + // println!("Verifier solidity: {verifier_solidity}"); + // // print vk_solidity + // println!("VK solidity: {vk_solidity}"); let vk_creation_code = compile_solidity(&vk_solidity); let vk_address = evm.create(vk_creation_code); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 8e5a841..92c4ff8 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -890,6 +890,27 @@ contract Halo2Verifier { coeff_len_data := mload(add(i, 0x20)) } } + // normalized_coeff_computations + { + let norm_coeff_data := mload(pcs_ptr) + success := batch_invert(success, 0, and(norm_coeff_data, 0xFFFF)) + norm_coeff_data := shr(16, norm_coeff_data) + let diff_0_inv := mload(0x00) + let mptr0 := and(norm_coeff_data, 0xFFFF) + norm_coeff_data := shr(16, norm_coeff_data) + mstore(mptr0, diff_0_inv) + for + { + let mptr := add(mptr0, 0x20) + let mptr_end := add(mptr0, and(norm_coeff_data, 0xFFFF)) + } + lt(mptr, mptr_end) + { mptr := add(mptr, 0x20) } + { + mstore(mptr, mulmod(mload(mptr), diff_0_inv, R)) + } + pcs_ptr := add(pcs_ptr, 0x20) + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 6930603..f352c24 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -89,6 +89,8 @@ contract Halo2VerifyingKey { {%- for coeff_word in pcs_computations.coeff_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] {%- endfor %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } From bcb85f71c3b8045077b46da168b0568f8a542b86 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 1 Aug 2024 15:16:00 -0500 Subject: [PATCH 43/65] *r_evals_computations --- src/codegen/pcs.rs | 366 ++++++++++++++++++++-------- templates/Halo2VerifierReusable.sol | 172 ++++++++++--- templates/Halo2VerifyingKey.sol | 8 +- 3 files changed, 409 insertions(+), 137 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index ca1ba51..61330c2 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -173,7 +173,7 @@ pub(crate) fn rotation_sets(queries: &[Query]) -> (BTreeSet, Vec = comm_queries .into_iter() .fold(Vec::::new(), |mut sets, (comm, queries)| { @@ -683,6 +683,11 @@ pub(crate) fn bdfg21_computations( } else { normalized_coeff_computations }; + let r_evals_computations = if separate { + Vec::new() + } else { + r_evals_computations.collect_vec() + }; chain![ [point_computations, vanishing_computations], @@ -703,6 +708,7 @@ pub struct PcsDataEncoded { pub(crate) vanishing_computations: Vec, pub(crate) coeff_computations: Vec, pub(crate) normalized_coeff_computations: U256, + pub(crate) r_evals_computations: Vec, } // implement length of PcsDataEncoded @@ -712,6 +718,7 @@ impl PcsDataEncoded { + self.vanishing_computations.len() + self.coeff_computations.len() + 1 // normalized_coeff_computations + + self.r_evals_computations.len() } } @@ -746,7 +753,7 @@ pub(crate) fn bdfg21_computations_separate( let mu_minus_point_mptr = point_mptr + superset.len(); let vanishing_0_mptr = mu_minus_point_mptr + superset.len(); let diff_mptr = vanishing_0_mptr + 1; - // let r_eval_mptr = diff_mptr + sets.len(); + let r_eval_mptr = diff_mptr + sets.len(); // let sum_mptr = r_eval_mptr + sets.len(); // let point_vars = @@ -966,112 +973,269 @@ pub(crate) fn bdfg21_computations_separate( packed_word }; + // 1. The LSG byte of the first word will contain the total number of words that contain the set_coeff and the r_eval ptr followed + // by the packed Vec. + // 2. The next set of words will contain the evaluation pointers. The first LSG byte will contain the number of words + // that contain the packed evaluation pointers. The rest of the bytes will contain the evaluation pointers or evaluation pointers + coeff pointer. + // depending on if it is a single rotation set or not. + // 3. Here is how the encoding of the single rotation set will look like: + // 3a. After the 1 byte that contains the number of words with the packed ptr data for the given set, we encode the coeffs[0] ptr, + // the first eval ptr of the first group. TODO we need to assert that there is only one group of evals for a single rotation set, and that the eval in the second group index + // 1 is the quotient eval ptr. + // 3b. Next we encode the number of eval ptrs in the eval_group, encoding the eval ptrs that follow until we reach the end of the eval_group, repeating the process for the next eval_group. + // 4. Here is how the encoding of the not single rotation set will look like: + // 4a. After the 1 byte that contains the number of words with the packed ptr data for the given set, the coeffs ptrs, + // and the eval ptrs. + // and the coeff.ptr(). Throw if set.rots().len > 5 b/c anything greater than 5 we can't pack all the ptr data into a single word. + + let r_evals_computations: Vec = { + let pack_value = |packed_word: &mut U256, value: usize, bit_counter: &mut usize| { + *packed_word |= U256::from(value) << *bit_counter; + *bit_counter += 16; + }; + let encode_coeff_length = |coeff_len: usize, single_rot_set: &mut usize| -> usize { + if coeff_len == 1 { + *single_rot_set += 1; + assert!( + *single_rot_set <= 1, + "Only one single rotation set in the r_evals_computations" + ); + coeff_len + } else { + assert!(coeff_len != 0, "The number of rotations in a set is 0"); + coeff_len * 16 + } + }; + + let r_evals_meta_data: Vec = { + let mut packed_words = vec![U256::from(0)]; + let mut bit_counter = 8; + + pack_value( + &mut packed_words[0], + diffs[1].ptr().value().as_usize(), + &mut bit_counter, + ); + pack_value( + &mut packed_words[0], + r_eval_mptr.value().as_usize(), + &mut bit_counter, + ); + + let mut last_idx = 0; + let mut single_rot_set = 0; + + for set in sets.iter() { + let coeff_len = set.rots().len(); + // if coeff_len is greater than 1 then we scale it by 16. + let encoded_length = encode_coeff_length(coeff_len, &mut single_rot_set); + + assert!( + encoded_length <= 256, + "The encoded length for r_evals exceeds 256 bits" + ); + + let next_bit_counter = bit_counter + 8; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + packed_words[last_idx] = U256::from(encoded_length); + } else { + pack_value( + &mut packed_words[last_idx], + encoded_length, + &mut bit_counter, + ); + } + bit_counter = next_bit_counter; + } + + let packed_words_len = packed_words.len(); + packed_words[0] |= U256::from(packed_words_len); + packed_words + }; + + let calculate_evals_len_offset = |evals: &[&Word]| -> (usize, usize) { + if evals.len() < 3 { + (evals.len(), 8 + (evals.len() * 16)) + } else { + (0, 8 + 32) + } + }; + + let pack_evals = |packed_words: &mut Vec, + evals: &[&Word], + evals_len: usize, + bit_counter: &mut usize, + last_idx: usize| { + if evals_len == 0 { + let mptr = evals[0].ptr(); + let mptr_end = evals[0].ptr() - evals.len(); + packed_words[last_idx] |= U256::from(mptr.value().as_usize()) << *bit_counter; + *bit_counter += 16; + packed_words[last_idx] |= U256::from(mptr_end.value().as_usize()) << *bit_counter; + *bit_counter += 16; + } else { + for eval in evals.iter() { + let eval_ptr = eval.ptr(); + packed_words[last_idx] |= + U256::from(eval_ptr.value().as_usize()) << *bit_counter; + *bit_counter += 16; + } + } + }; + + let process_single_rotation_set = + |set: &RotationSet, + coeffs: &Vec, + packed_words: &mut Vec, + bit_counter: &mut usize, + last_idx: &mut usize| { + let eval_groups = set.evals().iter().rev().fold( + Vec::>::new(), + |mut eval_groups, evals| { + let eval = &evals[0]; + if let Some(last_group) = eval_groups.last_mut() { + let last_eval = **last_group.last().unwrap(); + if last_eval.ptr().value().is_integer() + && last_eval.ptr() - 1 == eval.ptr() + { + last_group.push(eval) + } else { + eval_groups.push(vec![eval]) + } + eval_groups + } else { + vec![vec![eval]] + } + }, + ); + + let coeff_ptr = coeffs[0].ptr(); + let first_eval_ptr = eval_groups[0][0].ptr(); + assert!( + eval_groups[1][0].ptr().loc() == Location::Memory, + "The second eval group for a single rotation set should be memory but it is not" + ); + + pack_value( + &mut packed_words[0], + coeff_ptr.value().as_usize(), + bit_counter, + ); + pack_value( + &mut packed_words[0], + first_eval_ptr.value().as_usize(), + bit_counter, + ); + + for evals in eval_groups.iter().skip(2) { + let (evals_len, offset) = calculate_evals_len_offset(evals); + let next_bit_counter = *bit_counter + offset; + + if next_bit_counter > 256 { + *last_idx += 1; + packed_words.push(U256::from(0)); + packed_words[*last_idx] = U256::from(evals_len); + let mut new_bit_counter = 8; + pack_evals( + packed_words, + evals, + evals_len, + &mut new_bit_counter, + *last_idx, + ); + } else { + packed_words[*last_idx] |= U256::from(evals_len) << *bit_counter; + *bit_counter += 8; + pack_evals(packed_words, evals, evals_len, bit_counter, *last_idx); + } + + *bit_counter = next_bit_counter; + } + }; + + let process_multiple_rotation_set = + |set: &RotationSet, + coeffs: &Vec, + packed_words: &mut Vec, + bit_counter: &mut usize, + last_idx: &mut usize| { + for coeff in coeffs.iter() { + pack_value( + &mut packed_words[0], + coeff.ptr().value().as_usize(), + bit_counter, + ); + } + + for evals in set.evals().iter().rev() { + let offset = coeffs.len() * 16; + let next_bit_counter = *bit_counter + offset; + + if next_bit_counter > 256 { + *last_idx += 1; + packed_words.push(U256::from(0)); + let mut new_bit_counter = 0; + for eval in evals.iter() { + let eval_ptr = eval.ptr(); + packed_words[*last_idx] |= + U256::from(eval_ptr.value().as_usize()) << new_bit_counter; + new_bit_counter += 16; + } + } else { + for eval in evals.iter() { + let eval_ptr = eval.ptr(); + packed_words[*last_idx] |= + U256::from(eval_ptr.value().as_usize()) << *bit_counter; + *bit_counter += 16; + } + } + + *bit_counter = next_bit_counter; + } + }; + + let r_evals_data: Vec = izip!(&sets, &coeffs) + .flat_map(|(set, coeffs)| { + let mut packed_words = vec![U256::from(0)]; + let mut last_idx = 0; + let mut bit_counter = 8; + + if set.rots().len() == 1 { + process_single_rotation_set( + set, + coeffs, + &mut packed_words, + &mut bit_counter, + &mut last_idx, + ); + } else { + process_multiple_rotation_set( + set, + coeffs, + &mut packed_words, + &mut bit_counter, + &mut last_idx, + ); + } + + let packed_words_len = packed_words.len(); + packed_words[0] |= U256::from(packed_words_len); + packed_words + }) + .collect(); + chain!(r_evals_meta_data.into_iter(), r_evals_data.into_iter()).collect_vec() + }; + PcsDataEncoded { point_computations, vanishing_computations, coeff_computations, normalized_coeff_computations, + r_evals_computations, } - // let normalized_coeff_computations = chain![ - // [ - // format!("success := batch_invert(success, 0, {first_batch_invert_end})"), - // format!("let diff_0_inv := {diff_0}"), - // format!("mstore({}, diff_0_inv)", diffs[0].ptr()), - // ], - // for_loop( - // [ - // format!("let mptr := {}", diffs[0].ptr() + 1), - // format!("let mptr_end := {}", diffs[0].ptr() + sets.len()), - // ], - // "lt(mptr, mptr_end)", - // ["mptr := add(mptr, 0x20)".to_string()], - // ["mstore(mptr, mulmod(mload(mptr), diff_0_inv, R))".to_string()], - // ), - // ] - // .collect_vec(); - - // let r_evals_computations = izip!(0.., &sets, &coeffs, &diffs, &r_evals).map( - // |(set_idx, set, coeffs, set_coeff, r_eval)| { - // let is_single_rot_set = set.rots().len() == 1; - // chain![ - // is_single_rot_set.then(|| format!("let coeff := {}", coeffs[0])), - // [ - // format!("let zeta := mload({})", zeta).as_str(), - // "let r_eval := 0" - // ] - // .map(str::to_string), - // if is_single_rot_set { - // let eval_groups = set.evals().iter().rev().fold( - // Vec::>::new(), - // |mut eval_groups, evals| { - // let eval = &evals[0]; - // if let Some(last_group) = eval_groups.last_mut() { - // let last_eval = **last_group.last().unwrap(); - // if last_eval.ptr().value().is_integer() - // && last_eval.ptr() - 1 == eval.ptr() - // { - // last_group.push(eval) - // } else { - // eval_groups.push(vec![eval]) - // } - // eval_groups - // } else { - // vec![vec![eval]] - // } - // }, - // ); - // chain![eval_groups.iter().enumerate()] - // .flat_map(|(group_idx, evals)| { - // if evals.len() < 3 { - // chain![evals.iter().enumerate()] - // .flat_map(|(eval_idx, eval)| { - // let is_first_eval = group_idx == 0 && eval_idx == 0; - // let item = format!("mulmod(coeff, {eval}, R)"); - // chain![ - // (!is_first_eval).then(|| format!( - // "r_eval := mulmod(r_eval, zeta, R)" - // )), - // [format!("r_eval := addmod(r_eval, {item}, R)")], - // ] - // }) - // .collect_vec() - // } else { - // let item = "mulmod(coeff, calldataload(mptr), R)"; - // for_loop( - // [ - // format!("let mptr := {}", evals[0].ptr()), - // format!("let mptr_end := {}", evals[0].ptr() - evals.len()), - // ], - // "lt(mptr_end, mptr)".to_string(), - // ["mptr := sub(mptr, 0x20)".to_string()], - // [format!( - // "r_eval := addmod(mulmod(r_eval, zeta, R), {item}, R)" - // )], - // ) - // } - // }) - // .collect_vec() - // } else { - // chain![set.evals().iter().enumerate().rev()] - // .flat_map(|(idx, evals)| { - // chain![ - // izip!(evals, coeffs).map(|(eval, coeff)| { - // let item = format!("mulmod({coeff}, {eval}, R)"); - // format!("r_eval := addmod(r_eval, {item}, R)") - // }), - // (idx != 0).then(|| format!("r_eval := mulmod(r_eval, zeta, R)")), - // ] - // }) - // .collect_vec() - // }, - // (set_idx != 0).then(|| format!("r_eval := mulmod(r_eval, {set_coeff}, R)")), - // [format!("mstore({}, r_eval)", r_eval.ptr())], - // ] - // .collect_vec() - // }, - // ); - // let coeff_sums_computation = izip!(&coeffs, &sums).map(|(coeffs, sum)| { // let (coeff_0, rest_coeffs) = coeffs.split_first().unwrap(); // chain![ diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 92c4ff8..d7b338e 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -367,7 +367,80 @@ contract Halo2Verifier { } } - // Modulus + function single_rot_set(r_evals_data, ptr, num_words, zeta, quotient_eval) -> ret0, ret1 { + let coeff := mload(and(r_evals_data, 0xFFFF)) + r_evals_data := shr(16, r_evals_data) + let r_eval + r_eval := addmod(r_eval, mulmod(coeff, calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_evals_data := shr(16, r_evals_data) + r_eval := mulmod(r_eval, zeta, R) + r_eval := addmod(r_eval, mulmod(coeff, quotient_eval, R), R) + for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { + for { } r_evals_data { } { + let eval_group_len := and(r_evals_data, 0xFF) + r_evals_data := shr(8, r_evals_data) + switch eq(eval_group_len, 0x0) + case 0x0 { + for { let j := 0 } lt(j, eval_group_len) { j := add(j, 1) } { + r_eval := addmod(mulmod(r_eval, zeta, R), mulmod(coeff, calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_evals_data := shr(16, r_evals_data) + } + } default { + for + { + let mptr := and(r_evals_data, 0xFFFF) + r_evals_data := shr(16, r_evals_data) + let mptr_end := and(r_evals_data, 0xFFFF) + } + lt(mptr_end, mptr) + { mptr := sub(mptr, 0x20) } + { + r_eval := addmod(mulmod(r_eval, zeta, R), mulmod(coeff, calldataload(mptr), R), R) + } + r_evals_data := shr(16, r_evals_data) + } + } + ptr := add(ptr, 0x20) + r_evals_data := mload(ptr) + } + ret0 := r_eval + ret1 := ptr + } + + function multi_rot_set(r_evals_data, ptr, num_words, rot_len, zeta) -> ret0, ret1 { + let coeffs_ptrs := and(r_evals_data, sub(shl(rot_len, 1), 1)) + r_evals_data := shr(rot_len, r_evals_data) + let r_eval := 0 + for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { + for { } r_evals_data { } { + for { let j := 0 } lt(j, rot_len) { j := add(j, 16) } { + r_eval := addmod(r_eval, mulmod(mload(and(shr(j, coeffs_ptrs), 0xFFFF)), calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_evals_data := shr(16, r_evals_data) + } + // Only on the last index do we NOT execute this if block. + if or(r_evals_data, lt(i, sub(num_words, 1))) { + r_eval := mulmod(r_eval, zeta, R) + } + } + ptr := add(ptr, 0x20) + r_evals_data := mload(ptr) + } + ret0 := r_eval + ret1 := ptr + } + + function r_evals_computation(rot_len, r_evals_data_ptr, zeta, quotient_eval) -> ret0, ret1 { + let r_evals_data := mload(r_evals_data_ptr) + // number of words to encode the data needed for this set in the r_evals computation. + let num_words := and(r_evals_data, 0xFF) + r_evals_data := shr(8, r_evals_data) + switch rot_len + case 0x1 { + ret0, ret1 := single_rot_set(r_evals_data, r_evals_data_ptr, num_words, zeta, quotient_eval) + } default { + ret0, ret1 := multi_rot_set(r_evals_data, r_evals_data_ptr, num_words, rot_len, zeta) + } + } // Initialize success as true let success := true @@ -792,28 +865,28 @@ contract Halo2Verifier { // [X] point_computations // [x] vanishing_computation // [x] coeff_computations - // [] formalized_coeff_computations - // [] r_evals_computations + // [x] normalized_coeff_computations + // [x] r_evals_computations // [] coeff_sums_computation // [] r_eval_computations // [] pairing_input_computations { // point_computations let pcs_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["pcs_computations_len_offset"]|hex() }}))) - let pcs_computations := mload(pcs_ptr) { + let point_computations := mload(pcs_ptr) let x := mload(add(theta_mptr, 0x80)) let omega := mload(add(vk_mptr, {{ vk_const_offsets["omega"]|hex() }})) let omega_inv := mload(add(vk_mptr, {{ vk_const_offsets["omega_inv"]|hex() }})) let x_pow_of_omega := mulmod(x, omega, R) - x_pow_of_omega, pcs_ptr := point_rots(pcs_computations, pcs_ptr, 32, x_pow_of_omega, omega) + x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 32, x_pow_of_omega, omega) pcs_ptr := add(pcs_ptr, 0x20) - pcs_computations := mload(pcs_ptr) + point_computations := mload(pcs_ptr) // Compute interm point - mstore(and(pcs_computations, 0xFFFF), x) + mstore(and(point_computations, 0xFFFF), x) x_pow_of_omega := mulmod(x, omega_inv, R) - pcs_computations := shr(16, pcs_computations) - x_pow_of_omega, pcs_ptr := point_rots(pcs_computations, pcs_ptr, 48, x_pow_of_omega, omega_inv) + point_computations := shr(16, point_computations) + x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 48, x_pow_of_omega, omega_inv) pcs_ptr := add(pcs_ptr, 0x20) pop(x_pow_of_omega) } @@ -821,15 +894,15 @@ contract Halo2Verifier { // vanishing_computations { let mu := mload(add(theta_mptr, 0xE0)) - pcs_computations := mload(pcs_ptr) + let vanishing_computations := mload(pcs_ptr) mstore(0x20, 1) for { - let mptr := and(pcs_computations, 0xFFFF) - pcs_computations := shr(16, pcs_computations) - let mptr_end := and(pcs_computations, 0xFFFF) - pcs_computations := shr(16, pcs_computations) - let point_mptr := and(pcs_computations, 0xFFFF) + let mptr := and(vanishing_computations, 0xFFFF) + vanishing_computations := shr(16, vanishing_computations) + let mptr_end := and(vanishing_computations, 0xFFFF) + vanishing_computations := shr(16, vanishing_computations) + let point_mptr := and(vanishing_computations, 0xFFFF) } lt(mptr, mptr_end) { @@ -840,36 +913,36 @@ contract Halo2Verifier { mstore(mptr, addmod(mu, sub(R, mload(point_mptr)), R)) } pop(mu) - pcs_computations := shr(16, pcs_computations) + vanishing_computations := shr(16, vanishing_computations) let s - s := mload(and(pcs_computations, 0xFFFF)) - pcs_computations := shr(16, pcs_computations) - for { } pcs_computations { } { - s := mulmod(s, mload(and(pcs_computations, 0xFFFF)), R) - pcs_computations := shr(16, pcs_computations) + s := mload(and(vanishing_computations, 0xFFFF)) + vanishing_computations := shr(16, vanishing_computations) + for { } vanishing_computations { } { + s := mulmod(s, mload(and(vanishing_computations, 0xFFFF)), R) + vanishing_computations := shr(16, vanishing_computations) } pcs_ptr := add(pcs_ptr, 0x20) - pcs_computations := mload(pcs_ptr) - mstore(and(pcs_computations, 0xFFFF), s) - pcs_computations := shr(16, pcs_computations) + vanishing_computations := mload(pcs_ptr) + mstore(and(vanishing_computations, 0xFFFF), s) + vanishing_computations := shr(16, vanishing_computations) let diff - let sets_len := and(pcs_computations, 0xFFFF) + let sets_len := and(vanishing_computations, 0xFFFF) pcs_ptr := add(pcs_ptr, 0x20) - pcs_computations := mload(pcs_ptr) + vanishing_computations := mload(pcs_ptr) for { let i := 0 } lt(i, sets_len) { i := add(i, 1) } { - diff := mload(and(pcs_computations, 0xFFFF)) - pcs_computations := shr(16, pcs_computations) - for { } and(pcs_computations, 0xFFFF) { } { - diff := mulmod(diff, mload(and(pcs_computations, 0xFFFF)), R) - pcs_computations := shr(16, pcs_computations) + diff := mload(and(vanishing_computations, 0xFFFF)) + vanishing_computations := shr(16, vanishing_computations) + for { } and(vanishing_computations, 0xFFFF) { } { + diff := mulmod(diff, mload(and(vanishing_computations, 0xFFFF)), R) + vanishing_computations := shr(16, vanishing_computations) } - pcs_computations := shr(16, pcs_computations) - mstore(and(pcs_computations, 0xFFFF), diff) + vanishing_computations := shr(16, vanishing_computations) + mstore(and(vanishing_computations, 0xFFFF), diff) if eq(i, 0) { mstore(0x00, diff) } pcs_ptr := add(pcs_ptr, 0x20) - pcs_computations := mload(pcs_ptr) + vanishing_computations := mload(pcs_ptr) } } @@ -911,6 +984,37 @@ contract Halo2Verifier { } pcs_ptr := add(pcs_ptr, 0x20) } + // r_evals_computations + { + let r_evals_meta_data := mload(pcs_ptr) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(r_evals_meta_data, 0xFF))) + r_evals_meta_data := shr(8, r_evals_meta_data) + let set_coeff := and(r_evals_meta_data, 0xFFFF) + r_evals_meta_data := shr(16, r_evals_meta_data) + let r_eval_mptr := and(r_evals_meta_data, 0xFFFF) + r_evals_meta_data := shr(16, r_evals_meta_data) + let i := pcs_ptr + pcs_ptr := end_ptr_packed_lens + let zeta := mload(add(theta_mptr, 0xA0)) + let quotient_eval := mload(add(theta_mptr, 0x240)) + let not_first + let r_eval + for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { + for { } r_evals_meta_data { } { + // pass the is_single_rot_set: bool, r_evals_data word, + r_eval, pcs_ptr := r_evals_computation(and(r_evals_meta_data, 0xFF), pcs_ptr, zeta, quotient_eval) + r_evals_meta_data := shr(8, r_evals_meta_data) + if not_first { + r_eval := mulmod(r_eval, mload(set_coeff), R) + set_coeff := add(set_coeff, 0x20) + } + not_first := 1 + mstore(r_eval_mptr, r_eval) + r_eval_mptr := add(r_eval_mptr, 0x20) + } + r_evals_meta_data := mload(add(i, 0x20)) + } + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index f352c24..824e758 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -89,8 +89,12 @@ contract Halo2VerifyingKey { {%- for coeff_word in pcs_computations.coeff_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations + {%- let offset_0 = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} + mstore({{ (32 * offset_0)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations + {%- let offset_1 = offset_0 + 1 %} + {%- for r_eval_word in pcs_computations.r_evals_computations %} + mstore({{ (32 * (offset_1 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] + {%- endfor %} return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } From cfeea110e952fec45fe1c6e26122631e4218b30f Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 1 Aug 2024 21:10:13 -0500 Subject: [PATCH 44/65] *coeff_sums_computation --- src/codegen/pcs.rs | 53 +++++++++++++++++++++-------- templates/Halo2VerifierReusable.sol | 28 ++++++++++++++- templates/Halo2VerifyingKey.sol | 4 +++ 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 61330c2..a6e0535 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -688,6 +688,11 @@ pub(crate) fn bdfg21_computations( } else { r_evals_computations.collect_vec() }; + let coeff_sums_computation = if separate { + Vec::new() + } else { + coeff_sums_computation.collect_vec() + }; chain![ [point_computations, vanishing_computations], @@ -709,6 +714,7 @@ pub struct PcsDataEncoded { pub(crate) coeff_computations: Vec, pub(crate) normalized_coeff_computations: U256, pub(crate) r_evals_computations: Vec, + pub(crate) coeff_sums_computation: Vec, } // implement length of PcsDataEncoded @@ -719,6 +725,7 @@ impl PcsDataEncoded { + self.coeff_computations.len() + 1 // normalized_coeff_computations + self.r_evals_computations.len() + + self.coeff_sums_computation.len() } } @@ -754,7 +761,7 @@ pub(crate) fn bdfg21_computations_separate( let vanishing_0_mptr = mu_minus_point_mptr + superset.len(); let diff_mptr = vanishing_0_mptr + 1; let r_eval_mptr = diff_mptr + sets.len(); - // let sum_mptr = r_eval_mptr + sets.len(); + let sum_mptr = r_eval_mptr + sets.len(); // let point_vars = // izip!(&superset, (0..).map(|idx| format!("point_{idx}"))).collect::>(); @@ -764,7 +771,7 @@ pub(crate) fn bdfg21_computations_separate( let vanishing_0 = Word::from(vanishing_0_mptr); let diffs = Word::range(diff_mptr).take(sets.len()).collect_vec(); // let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); - // let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); + let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. // Otherwise we load in omega and omega_inv from the solidity constants. @@ -1228,26 +1235,44 @@ pub(crate) fn bdfg21_computations_separate( chain!(r_evals_meta_data.into_iter(), r_evals_data.into_iter()).collect_vec() }; + let coeff_sums_computation: Vec = { + let mut packed_words = vec![U256::from(0)]; + let mut bit_counter = 8; + let mut last_idx = 0; + for (coeffs, sum) in izip!(&coeffs, &sums) { + let offset = 24; + let next_bit_counter = bit_counter + offset; + let len_minus_one = coeffs.len() * 32; + assert!( + len_minus_one <= 256, + "The length of the coeffs exceeds 256 bits", + ); + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + packed_words[last_idx] |= U256::from(len_minus_one); + packed_words[last_idx] |= U256::from(sum.ptr().value().as_usize()) << 8; + } else { + packed_words[last_idx] |= U256::from(len_minus_one) << bit_counter; + bit_counter += 8; + packed_words[last_idx] |= U256::from(sum.ptr().value().as_usize()) << bit_counter; + } + bit_counter = next_bit_counter; + } + let packed_words_len = packed_words.len(); + packed_words[0] |= U256::from(packed_words_len); + packed_words + }; + PcsDataEncoded { point_computations, vanishing_computations, coeff_computations, normalized_coeff_computations, r_evals_computations, + coeff_sums_computation, } - // let coeff_sums_computation = izip!(&coeffs, &sums).map(|(coeffs, sum)| { - // let (coeff_0, rest_coeffs) = coeffs.split_first().unwrap(); - // chain![ - // [format!("let sum := {coeff_0}")], - // rest_coeffs - // .iter() - // .map(|coeff_mptr| format!("sum := addmod(sum, {coeff_mptr}, R)")), - // [format!("mstore({}, sum)", sum.ptr())], - // ] - // .collect_vec() - // }); - // let nu = get_memory_ptr(theta_mptr, 6, &separate); // let mu = get_memory_ptr(theta_mptr, 7, &separate); // let r_eval = get_memory_ptr(theta_mptr, 21, &separate); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index d7b338e..13310fc 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -867,7 +867,7 @@ contract Halo2Verifier { // [x] coeff_computations // [x] normalized_coeff_computations // [x] r_evals_computations - // [] coeff_sums_computation + // [x] coeff_sums_computation // [] r_eval_computations // [] pairing_input_computations { @@ -985,6 +985,8 @@ contract Halo2Verifier { pcs_ptr := add(pcs_ptr, 0x20) } // r_evals_computations + // TODO optimizize this by hardcoding the coeff_ptr and iterating it + // over the coeff_len_data. Will reduce VK size. { let r_evals_meta_data := mload(pcs_ptr) let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(r_evals_meta_data, 0xFF))) @@ -1015,6 +1017,30 @@ contract Halo2Verifier { r_evals_meta_data := mload(add(i, 0x20)) } } + // coeff_sums_computation + { + let coeff_sums_data := mload(pcs_ptr) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_sums_data, 0xFF))) + coeff_sums_data := shr(8, coeff_sums_data) + let coeff_ptr := 0x20 + let i := pcs_ptr + pcs_ptr := end_ptr_packed_lens + for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { + for { } coeff_sums_data { } { + let sum := mload(coeff_ptr) + let len := and(coeff_sums_data, 0xFF) + coeff_sums_data := shr(8, coeff_sums_data) + for { let j := 0x20 } lt(j, len) { j := add(j, 0x20) } { + sum := addmod(sum, mload(add(coeff_ptr, j)), R) + } + coeff_ptr := add(coeff_ptr, len) + mstore(and(coeff_sums_data, 0xFFFF), sum) + coeff_sums_data := shr(16, coeff_sums_data) + } + coeff_sums_data := mload(add(i, 0x20)) + } + + } {%- for code_block in pcs_computations %} { {%- for line in code_block %} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 824e758..b9efe67 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -95,6 +95,10 @@ contract Halo2VerifyingKey { {%- for r_eval_word in pcs_computations.r_evals_computations %} mstore({{ (32 * (offset_1 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] {%- endfor %} + {%- let offset_2 = offset_1 + pcs_computations.r_evals_computations.len() %} + {%- for coeff_sum in pcs_computations.coeff_sums_computation %} + mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ coeff_sum|hex_padded(64) }}) // coeff_sums_computation[{{ loop.index0 }}] + {%- endfor %} return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } From b2b636cfc49ee36d243cd46933cc338ed31e10af Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 3 Aug 2024 01:08:54 -0500 Subject: [PATCH 45/65] *r_eval + pairing computations --- src/codegen.rs | 28 -- src/codegen/pcs.rs | 471 +++++++++++----------------- src/codegen/template.rs | 1 - templates/Halo2VerifierReusable.sol | 270 +++++++++++++++- templates/Halo2VerifyingKey.sol | 10 +- 5 files changed, 448 insertions(+), 332 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 3a0ce4c..81b4b8a 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -570,42 +570,14 @@ impl<'a> SolidityGenerator<'a> { } fn generate_separate_verifier(&self) -> Halo2VerifierReusable { - let proof_cptr = Ptr::calldata(0x84); - - let vk = self.generate_vk(true); - let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr, true); - let vk_mptr = Ptr::memory(vk_m); - // if separate then create a hashmap of vk.const_expressions values to its vk memory location. - let mut vk_lookup_const_table: HashMap, Ptr> = HashMap::new(); - // create hashmap of vk.const_expressions values to its vk memory location. - let offset = vk_m - + (vk.constants.len() * 0x20) - + (vk.fixed_comms.len() + vk.permutation_comms.len()) * 0x40; - // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. - vk.const_expressions - .iter() - .enumerate() - .for_each(|(idx, _)| { - let mptr = offset + (0x20 * idx); - let mptr = Ptr::memory(mptr); - vk_lookup_const_table.insert(vk.const_expressions[idx], mptr); - }); - - let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, true); let vk_const_offsets: HashMap<&'static str, U256> = Self::dummy_vk_constants(true) .iter() .enumerate() .map(|(idx, &(key, _))| (key, U256::from(idx * 32))) .collect(); - let pcs_computations = match self.scheme { - Bdfg21 => bdfg21_computations(&self.meta, &data, true), - Gwc19 => unimplemented!(), - }; - Halo2VerifierReusable { scheme: self.scheme, - pcs_computations, vk_const_offsets, } } diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index a6e0535..82d9561 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -552,7 +552,6 @@ pub(crate) fn bdfg21_computations( [format!("mstore({r_eval}, r_eval)")], ] .collect_vec(); - let pairing_input_computations = chain![ [format!("let nu := mload({nu})").to_string()], izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, set_coeff)| { @@ -663,37 +662,6 @@ pub(crate) fn bdfg21_computations( ] .collect_vec(); - let point_computations = if separate { - Vec::new() - } else { - point_computations - }; - let vanishing_computations = if separate { - Vec::new() - } else { - vanishing_computations - }; - let coeff_computations = if separate { - Vec::new() - } else { - coeff_computations - }; - let normalized_coeff_computations = if separate { - Vec::new() - } else { - normalized_coeff_computations - }; - let r_evals_computations = if separate { - Vec::new() - } else { - r_evals_computations.collect_vec() - }; - let coeff_sums_computation = if separate { - Vec::new() - } else { - coeff_sums_computation.collect_vec() - }; - chain![ [point_computations, vanishing_computations], coeff_computations, @@ -715,6 +683,8 @@ pub struct PcsDataEncoded { pub(crate) normalized_coeff_computations: U256, pub(crate) r_evals_computations: Vec, pub(crate) coeff_sums_computation: Vec, + pub(crate) r_eval_computations: U256, + pub(crate) pairing_input_computations: Vec, } // implement length of PcsDataEncoded @@ -726,6 +696,8 @@ impl PcsDataEncoded { + 1 // normalized_coeff_computations + self.r_evals_computations.len() + self.coeff_sums_computation.len() + + 1 // r_eval_computations + + self.pairing_input_computations.len() } } @@ -739,8 +711,8 @@ pub(crate) fn bdfg21_computations_separate( let max_rot = *superset.last().unwrap(); let num_coeffs = sets.iter().map(|set| set.rots().len()).sum::(); - // let w = EcPoint::from(data.w_cptr); - // let w_prime = EcPoint::from(data.w_cptr + 2); + let w = EcPoint::from(data.w_cptr); + let w_prime = EcPoint::from(data.w_cptr + 2); let diff_0 = Word::from(Ptr::memory(0x00)); let coeffs = sets @@ -753,7 +725,7 @@ pub(crate) fn bdfg21_computations_separate( .collect_vec(); let first_batch_invert_end = diff_0.ptr() + 1 + num_coeffs; - // let second_batch_invert_end = diff_0.ptr() + sets.len(); + let second_batch_invert_end = diff_0.ptr() + sets.len(); let free_mptr = diff_0.ptr() + 2 * (1 + num_coeffs) + 6; let point_mptr = free_mptr; @@ -763,32 +735,14 @@ pub(crate) fn bdfg21_computations_separate( let r_eval_mptr = diff_mptr + sets.len(); let sum_mptr = r_eval_mptr + sets.len(); - // let point_vars = - // izip!(&superset, (0..).map(|idx| format!("point_{idx}"))).collect::>(); let points = izip!(&superset, Word::range(point_mptr)).collect::>(); let mu_minus_points = izip!(&superset, Word::range(mu_minus_point_mptr)).collect::>(); let vanishing_0 = Word::from(vanishing_0_mptr); let diffs = Word::range(diff_mptr).take(sets.len()).collect_vec(); - // let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); + let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); - // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. - // Otherwise we load in omega and omega_inv from the solidity constants. - - // let vk_mptr = "vk_mptr"; - // let theta_mptr = "theta_mptr"; - - // let omega = get_memory_ptr(vk_mptr, 10, &separate); - - // let omega_inv = get_memory_ptr(vk_mptr, 11, &separate); - - // let g1_x = get_memory_ptr(vk_mptr, 17, &separate); - // let g1_y = get_memory_ptr(vk_mptr, 18, &separate); - - // let x = get_memory_ptr(theta_mptr, 4, &separate); - - // let zeta = get_memory_ptr(theta_mptr, 5, &separate); let point_computations: Vec = { let pack_words = |points: Vec, interm_point: Option| { let mut packed_words: Vec = vec![U256::from(0)]; @@ -806,16 +760,14 @@ pub(crate) fn bdfg21_computations_separate( for point in points.iter() { let offset = 16; - let mut next_bit_counter = bit_counter + offset; + let next_bit_counter = bit_counter + offset; if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); - next_bit_counter = offset; - packed_words[last_idx] = *point - } else { - packed_words[last_idx] |= *point << bit_counter; + bit_counter = 0; } - bit_counter = next_bit_counter; + packed_words[last_idx] |= *point << bit_counter; + bit_counter += 16; } packed_words @@ -922,16 +874,14 @@ pub(crate) fn bdfg21_computations_separate( let coeff_len = set.rots().len(); assert!(coeff_len <= 5, "The number of rotations in a set exceeds 5 for the coeff_computations. Can't pack all the coef_data in a single word"); let offset = 8; - let mut next_bit_counter = bit_counter + offset; + let next_bit_counter = bit_counter + offset; if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); - next_bit_counter = offset; - packed_words[last_idx] = U256::from(coeff_len) - } else { - packed_words[last_idx] |= U256::from(coeff_len) << bit_counter; + bit_counter = 0; } - bit_counter = next_bit_counter; + packed_words[last_idx] |= U256::from(coeff_len) << bit_counter; + bit_counter += 8; } let packed_words_len = packed_words.len(); // Encode the length of the exprs vec in the first word @@ -1038,7 +988,7 @@ pub(crate) fn bdfg21_computations_separate( let encoded_length = encode_coeff_length(coeff_len, &mut single_rot_set); assert!( - encoded_length <= 256, + encoded_length < 256, "The encoded length for r_evals exceeds 256 bits" ); @@ -1046,15 +996,10 @@ pub(crate) fn bdfg21_computations_separate( if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); - packed_words[last_idx] = U256::from(encoded_length); - } else { - pack_value( - &mut packed_words[last_idx], - encoded_length, - &mut bit_counter, - ); + bit_counter = 0; } - bit_counter = next_bit_counter; + packed_words[last_idx] |= U256::from(encoded_length) << bit_counter; + bit_counter += 8; } let packed_words_len = packed_words.len(); @@ -1143,22 +1088,12 @@ pub(crate) fn bdfg21_computations_separate( if next_bit_counter > 256 { *last_idx += 1; packed_words.push(U256::from(0)); - packed_words[*last_idx] = U256::from(evals_len); - let mut new_bit_counter = 8; - pack_evals( - packed_words, - evals, - evals_len, - &mut new_bit_counter, - *last_idx, - ); - } else { - packed_words[*last_idx] |= U256::from(evals_len) << *bit_counter; - *bit_counter += 8; - pack_evals(packed_words, evals, evals_len, bit_counter, *last_idx); + *bit_counter = 0; } - *bit_counter = next_bit_counter; + packed_words[*last_idx] |= U256::from(evals_len) << *bit_counter; + *bit_counter += 8; + pack_evals(packed_words, evals, evals_len, bit_counter, *last_idx); } }; @@ -1183,23 +1118,14 @@ pub(crate) fn bdfg21_computations_separate( if next_bit_counter > 256 { *last_idx += 1; packed_words.push(U256::from(0)); - let mut new_bit_counter = 0; - for eval in evals.iter() { - let eval_ptr = eval.ptr(); - packed_words[*last_idx] |= - U256::from(eval_ptr.value().as_usize()) << new_bit_counter; - new_bit_counter += 16; - } - } else { - for eval in evals.iter() { - let eval_ptr = eval.ptr(); - packed_words[*last_idx] |= - U256::from(eval_ptr.value().as_usize()) << *bit_counter; - *bit_counter += 16; - } + *bit_counter = 0; + } + for eval in evals.iter() { + let eval_ptr = eval.ptr(); + packed_words[*last_idx] |= + U256::from(eval_ptr.value().as_usize()) << *bit_counter; + *bit_counter += 16; } - - *bit_counter = next_bit_counter; } }; @@ -1242,28 +1168,169 @@ pub(crate) fn bdfg21_computations_separate( for (coeffs, sum) in izip!(&coeffs, &sums) { let offset = 24; let next_bit_counter = bit_counter + offset; - let len_minus_one = coeffs.len() * 32; - assert!( - len_minus_one <= 256, - "The length of the coeffs exceeds 256 bits", - ); + let len = coeffs.len() * 32; + assert!(len < 256, "The length of the coeffs exceeds 256 bits",); if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); - packed_words[last_idx] |= U256::from(len_minus_one); - packed_words[last_idx] |= U256::from(sum.ptr().value().as_usize()) << 8; - } else { - packed_words[last_idx] |= U256::from(len_minus_one) << bit_counter; - bit_counter += 8; - packed_words[last_idx] |= U256::from(sum.ptr().value().as_usize()) << bit_counter; + bit_counter = 0; } - bit_counter = next_bit_counter; + packed_words[last_idx] |= U256::from(len) << bit_counter; + bit_counter += 8; + packed_words[last_idx] |= U256::from(sum.ptr().value().as_usize()) << bit_counter; + bit_counter += 16; } let packed_words_len = packed_words.len(); packed_words[0] |= U256::from(packed_words_len); packed_words }; + let r_eval_computations: U256 = { + let mut packed_word = U256::from(0); + let mut offset = 0; + packed_word |= U256::from(second_batch_invert_end.value().as_usize()) << offset; + offset += 16; + packed_word |= U256::from(sums[0].ptr().value().as_usize()) << offset; + offset += 16; + packed_word |= U256::from(r_evals.last().unwrap().ptr().value().as_usize()) << offset; + packed_word + }; + + let pairing_input_computations: Vec = { + let mut word_lengths = Vec::new(); + let data: Vec = sets + .iter() + .flat_map(|set| { + let comm_groups = set.comms().iter().rev().skip(1).fold( + Vec::<(Location, Vec<&EcPoint>)>::new(), + |mut comm_groups, comm| { + if let Some(last_group) = comm_groups.last_mut() { + let last_comm = **last_group.1.last().unwrap(); + if last_group.0 == comm.loc() + && last_comm.x().ptr().value().is_integer() + && last_comm.x().ptr() - 2 == comm.x().ptr() + { + last_group.1.push(comm) + } else { + comm_groups.push((comm.loc(), vec![comm])) + } + comm_groups + } else { + vec![(comm.loc(), vec![comm])] + } + }, + ); + let mut packed_words = vec![U256::from(0)]; + let mut bit_counter = 0; + let mut last_idx = 0; + let comm = set.comms().last().unwrap(); + packed_words[last_idx] |= + U256::from(comm.x().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(comm.y().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + for (loc, comms) in comm_groups.iter() { + let is_quotient_point = !comms[0].x().ptr().value().is_integer() + && !comms[0].y().ptr().value().is_integer(); + let offset = if comms.len() == 2 { 80 } else if is_quotient_point { 16 } else { 48 } ; + let next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + let loc_encoded = if is_quotient_point + { + assert!( + comms.len() == 1, + "The number of comms in the group containing the quotient points must be 1", + ); + // we encode 0x02 if the comm is a quotient point + 0x02 + } else { + // check the location of the comm. If it is memory then we encode 0x00, 0x01 otherwise + if *loc == Location::Memory { 0x00 } else { 0x01 } + }; + packed_words[last_idx] |= U256::from(loc_encoded) << bit_counter; + bit_counter += 8; + let len_encoded = if comms.len() < 3 { comms.len() } else { 0 }; + packed_words[last_idx] |= U256::from(len_encoded) << bit_counter; + bit_counter += 8; + if loc_encoded == 0x02 { + // we hardcode the location of the quotient points in reusable verifier so we skip encoding them + // in the vk + continue; + } + if comms.len() < 3 { + comms.iter().for_each(|comm| { + packed_words[last_idx] |= + U256::from(comm.x().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(comm.y().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + }); + } else { + let mptr = comms.first().unwrap().x().ptr(); + let mptr_end = mptr - 2 * comms.len(); + packed_words[last_idx] |= + U256::from(mptr.value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(mptr_end.value().as_usize()) << bit_counter; + bit_counter += 16; + } + } + assert!( + packed_words.len() * 32 < 256, + "The bit counter for the pairing input computations exceeds 256 bits" + ); + // update the word lengths + word_lengths.push(packed_words.len() * 32); + packed_words + }) + .collect(); + + let meta_data: Vec = { + let mut packed_words = vec![U256::from(0)]; + let mut bit_counter = 8; + let mut last_idx = 0; + packed_words[last_idx] |= U256::from(diffs[1].ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + // pack the ec points cptrs + packed_words[last_idx] |= U256::from(w.x().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= U256::from(w.y().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(vanishing_0.ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(w_prime.x().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= + U256::from(w_prime.y().ptr().value().as_usize()) << bit_counter; + bit_counter += 16; + + // iterate through the word lengths and pack them into the first word + for len in word_lengths.iter() { + let next_bit_counter = bit_counter + 8; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + packed_words[last_idx] |= U256::from(*len) << bit_counter; + bit_counter += 8; + } + let packed_words_len = packed_words.len(); + packed_words[0] |= U256::from(packed_words_len); + packed_words + }; + chain!(meta_data.into_iter(), data.into_iter()).collect_vec() + }; + PcsDataEncoded { point_computations, vanishing_computations, @@ -1271,173 +1338,7 @@ pub(crate) fn bdfg21_computations_separate( normalized_coeff_computations, r_evals_computations, coeff_sums_computation, + r_eval_computations, + pairing_input_computations, } - - // let nu = get_memory_ptr(theta_mptr, 6, &separate); - // let mu = get_memory_ptr(theta_mptr, 7, &separate); - // let r_eval = get_memory_ptr(theta_mptr, 21, &separate); - // let pairing_lhs_x = get_memory_ptr(theta_mptr, 22, &separate); - // let pairing_lhs_y = get_memory_ptr(theta_mptr, 23, &separate); - // let pairing_rhs_x = get_memory_ptr(theta_mptr, 24, &separate); - // let pairing_rhs_y = get_memory_ptr(theta_mptr, 25, &separate); - // let r_eval_computations = chain![ - // for_loop( - // [ - // format!("let mptr := 0x00"), - // format!("let mptr_end := {second_batch_invert_end}"), - // format!("let sum_mptr := {}", sums[0].ptr()), - // ], - // "lt(mptr, mptr_end)", - // ["mptr := add(mptr, 0x20)", "sum_mptr := add(sum_mptr, 0x20)"].map(str::to_string), - // ["mstore(mptr, mload(sum_mptr))".to_string()], - // ), - // [ - // format!("success := batch_invert(success, 0, {second_batch_invert_end})"), - // format!( - // "let r_eval := mulmod(mload({}), {}, R)", - // second_batch_invert_end - 1, - // r_evals.last().unwrap() - // ) - // ], - // for_loop( - // [ - // format!("let sum_inv_mptr := {}", second_batch_invert_end - 2), - // format!("let sum_inv_mptr_end := {second_batch_invert_end}"), - // format!("let r_eval_mptr := {}", r_evals[r_evals.len() - 2].ptr()), - // ], - // "lt(sum_inv_mptr, sum_inv_mptr_end)", - // [ - // "sum_inv_mptr := sub(sum_inv_mptr, 0x20)", - // "r_eval_mptr := sub(r_eval_mptr, 0x20)" - // ] - // .map(str::to_string), - // [ - // format!("r_eval := mulmod(r_eval, mload({nu}), R)").as_str(), - // "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), R), R)" - // ] - // .map(str::to_string), - // ), - // [format!("mstore({r_eval}, r_eval)")], - // ] - // .collect_vec(); - - // let pairing_input_computations = chain![ - // [format!("let nu := mload({nu})").to_string()], - // izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, set_coeff)| { - // let is_first_set = set_idx == 0; - // let is_last_set = set_idx == sets.len() - 1; - - // let ec_add = &format!("ec_add_{}", if is_first_set { "acc" } else { "tmp" }); - // let ec_mul = &format!("ec_mul_{}", if is_first_set { "acc" } else { "tmp" }); - // let acc_x = Ptr::memory(0x00) + if is_first_set { 0 } else { 4 }; - // let acc_y = acc_x + 1; - - // let comm_groups = set.comms().iter().rev().skip(1).fold( - // Vec::<(Location, Vec<&EcPoint>)>::new(), - // |mut comm_groups, comm| { - // if let Some(last_group) = comm_groups.last_mut() { - // let last_comm = **last_group.1.last().unwrap(); - // if last_group.0 == comm.loc() - // && last_comm.x().ptr().value().is_integer() - // && last_comm.x().ptr() - 2 == comm.x().ptr() - // { - // last_group.1.push(comm) - // } else { - // comm_groups.push((comm.loc(), vec![comm])) - // } - // comm_groups - // } else { - // vec![(comm.loc(), vec![comm])] - // } - // }, - // ); - // let zeta_mptr = ζ - // chain![ - // set.comms() - // .last() - // .map(|comm| { - // [ - // format!("mstore({acc_x}, {})", comm.x()), - // format!("mstore({acc_y}, {})", comm.y()), - // ] - // }) - // .into_iter() - // .flatten(), - // comm_groups.into_iter().flat_map(move |(loc, comms)| { - // if comms.len() < 3 { - // comms - // .iter() - // .flat_map(|comm| { - // let (x, y) = (comm.x(), comm.y()); - // [ - // format!("success := {ec_mul}(success, mload({zeta_mptr}))"), - // format!("success := {ec_add}(success, {x}, {y})"), - // ] - // }) - // .collect_vec() - // } else { - // let mptr = comms.first().unwrap().x().ptr(); - // let mptr_end = mptr - 2 * comms.len(); - // let x = Word::from(Ptr::new(loc, "mptr")); - // let y = Word::from(Ptr::new(loc, "add(mptr, 0x20)")); - // for_loop( - // [ - // format!("let mptr := {mptr}"), - // format!("let mptr_end := {mptr_end}"), - // ], - // "lt(mptr_end, mptr)", - // ["mptr := sub(mptr, 0x40)".to_string()], - // [ - // format!("success := {ec_mul}(success, mload({zeta_mptr}))"), - // format!("success := {ec_add}(success, {x}, {y})"), - // ], - // ) - // } - // }), - // (!is_first_set) - // .then(|| { - // let scalar = format!("mulmod(nu, {set_coeff}, R)"); - // chain![ - // [ - // format!("success := ec_mul_tmp(success, {scalar})"), - // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - // ], - // (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), R)")) - // ] - // }) - // .into_iter() - // .flatten(), - // ] - // .collect_vec() - // }), - // [ - // format!("mstore(0x80, mload({}))", g1_x), - // format!("mstore(0xa0, mload({}))", g1_y), - // format!("success := ec_mul_tmp(success, sub(R, mload({r_eval})))"), - // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - // format!("mstore(0x80, {})", w.x()), - // format!("mstore(0xa0, {})", w.y()), - // format!("success := ec_mul_tmp(success, sub(R, {vanishing_0}))"), - // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - // format!("mstore(0x80, {})", w_prime.x()), - // format!("mstore(0xa0, {})", w_prime.y()), - // format!("success := ec_mul_tmp(success, mload({mu}))"), - // format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - // format!("mstore({pairing_lhs_x}, mload(0x00))"), - // format!("mstore({pairing_lhs_y}, mload(0x20))"), - // format!("mstore({pairing_rhs_x}, {})", w_prime.x()), - // format!("mstore({pairing_rhs_y}, {})", w_prime.y()), - // ], - // ] - // .collect_vec(); - - // chain![ - // [point_computations, vanishing_computations], - // coeff_computations, - // [normalized_coeff_computations], - // r_evals_computations, - // coeff_sums_computation, - // [r_eval_computations, pairing_input_computations], - // ] - // .collect_vec() } diff --git a/src/codegen/template.rs b/src/codegen/template.rs index de13d62..7c1ceab 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -64,7 +64,6 @@ pub(crate) struct Halo2Verifier { #[template(path = "Halo2VerifierReusable.sol")] pub(crate) struct Halo2VerifierReusable { pub(crate) scheme: BatchOpenScheme, - pub(crate) pcs_computations: Vec>, pub(crate) vk_const_offsets: HashMap<&'static str, U256>, } diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 13310fc..3596712 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -442,6 +442,162 @@ contract Halo2Verifier { } } + function pairing_input_computations_first(len, pcs_ptr, data, theta_mptr, success) -> ret { + mstore(0x00, calldataload(and(data, 0xFFFF))) + data := shr(16, data) + mstore(0x20, calldataload(and(data, 0xFFFF))) + data := shr(16, data) + for { let i := 0 } lt(i, len) { i := add(i, 0x20) } { + for { } data { } { + let ptr_loc := and(data, 0xFF) + data := shr(8, data) + let comm_len := and(data, 0xFF) + data := shr(8, data) + switch comm_len + case 0x0 { + switch ptr_loc + case 0x00 { + for + { + let mptr := and(data, 0xFFFF) + data := shr(16, data) + let mptr_end := and(data, 0xFFFF) + } + lt(mptr_end, mptr) + { mptr := sub(mptr, 0x40) } + { + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, mload(mptr), mload(add(mptr, 0x20))) + } + } + case 0x01 { + for + { + let mptr := and(data, 0xFFFF) + data := shr(16, data) + let mptr_end := and(data, 0xFFFF) + } + lt(mptr_end, mptr) + { mptr := sub(mptr, 0x40) } + { + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, calldataload(mptr), calldataload(add(mptr, 0x20))) + } + } + data := shr(16, data) + } default { + switch ptr_loc + case 0x00 { + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + if eq(comm_len, 0x02) { + data := shr(32, data) + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + } + data := shr(32, data) + } + case 0x01 { + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + if eq(comm_len, 0x02) { + data := shr(32, data) + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + } + data := shr(32, data) + } + // Quotient eval x and y points + case 0x02 { + success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_acc(success, mload(add(theta_mptr, 0x260)), mload(add(theta_mptr, 0x280))) + } + } + } + pcs_ptr := add(pcs_ptr, 0x20) + data := mload(pcs_ptr) + } + ret := success + } + + function pairing_input_computations(len, pcs_ptr, data, theta_mptr, success) -> ret { + mstore(0x80, calldataload(and(data, 0xFFFF))) + data := shr(16, data) + mstore(0xa0, calldataload(and(data, 0xFFFF))) + data := shr(16, data) + for { let i := 0 } lt(i, len) { i := add(i, 0x20) } { + for { } data { } { + let ptr_loc := and(data, 0xFF) + data := shr(8, data) + let comm_len := and(data, 0xFF) + data := shr(8, data) + switch comm_len + case 0x0 { + switch ptr_loc + case 0x00 { + for + { + let mptr := and(data, 0xFFFF) + data := shr(16, data) + let mptr_end := and(data, 0xFFFF) + } + lt(mptr_end, mptr) + { mptr := sub(mptr, 0x40) } + { + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, mload(mptr), mload(add(mptr, 0x20))) + } + } + case 0x01 { + for + { + let mptr := and(data, 0xFFFF) + data := shr(16, data) + let mptr_end := and(data, 0xFFFF) + } + lt(mptr_end, mptr) + { mptr := sub(mptr, 0x40) } + { + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, calldataload(mptr), calldataload(add(mptr, 0x20))) + } + } + data := shr(16, data) + } default { + switch ptr_loc + case 0x00 { + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + if eq(comm_len, 0x2) { + data := shr(32, data) + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + } + data := shr(32, data) + } + case 0x01 { + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + if eq(comm_len, 0x2) { + data := shr(32, data) + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + } + data := shr(32, data) + } + // Quotient eval x and y points + case 0x02 { + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) + success := ec_add_tmp(success, mload(add(theta_mptr, 0x260)), mload(add(theta_mptr, 0x280))) + } + } + } + pcs_ptr := add(pcs_ptr, 0x20) + data := mload(pcs_ptr) + } + ret := success + } + // Initialize success as true let success := true // Initialize vk_mptr as 0x0 on the stack @@ -861,15 +1017,6 @@ contract Halo2Verifier { } // Compute pairing lhs and rhs - // TODO: - // [X] point_computations - // [x] vanishing_computation - // [x] coeff_computations - // [x] normalized_coeff_computations - // [x] r_evals_computations - // [x] coeff_sums_computation - // [] r_eval_computations - // [] pairing_input_computations { // point_computations let pcs_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["pcs_computations_len_offset"]|hex() }}))) @@ -945,12 +1092,11 @@ contract Halo2Verifier { vanishing_computations := mload(pcs_ptr) } } - // coeff_computations { let coeff_len_data := mload(pcs_ptr) // Load in the least significant byte of the `coeff_len_data` word to get the total number of words we will need to load in - // that contain the packed Vec. + // that contains the packed Vec. let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_len_data, 0xFF))) coeff_len_data := shr(8, coeff_len_data) let i := pcs_ptr @@ -1041,13 +1187,105 @@ contract Halo2Verifier { } } - {%- for code_block in pcs_computations %} + // r_eval_computation + { + let r_eval_data := mload(pcs_ptr) + let mptr_end := and(r_eval_data, 0xFFFF) + for + { + let mptr := 0x00 + r_eval_data := shr(16, r_eval_data) + let sum_mptr := and(r_eval_data, 0xFFFF) + } + lt(mptr, mptr_end) + { + mptr := add(mptr, 0x20) + sum_mptr := add(sum_mptr, 0x20) + } + { + mstore(mptr, mload(sum_mptr)) + } + r_eval_data := shr(16, r_eval_data) + success := batch_invert(success, 0, mptr_end) + let r_eval_ptr := and(r_eval_data, 0xFFFF) + let r_eval := mulmod(mload(sub(mptr_end, 0x20)), mload(r_eval_ptr), R) + r_eval_data := shr(16, r_eval_data) + for + { + let sum_inv_mptr := sub(mptr_end, 0x40) + let sum_inv_mptr_end := mptr_end + let r_eval_mptr := sub(r_eval_ptr, 0x20) + } + lt(sum_inv_mptr, sum_inv_mptr_end) + { + sum_inv_mptr := sub(sum_inv_mptr, 0x20) + r_eval_mptr := sub(r_eval_mptr, 0x20) + } + { + r_eval := mulmod(r_eval, mload(add(theta_mptr, 0xC0)), R) + r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), R), R) + } + mstore(add(theta_mptr, 0x2A0), r_eval) + pcs_ptr := add(pcs_ptr, 0x20) + } + // pairing_input_computations + let nu := mload(add(theta_mptr, 0xC0)) { - {%- for line in code_block %} - {{ line }} - {%- endfor %} + let pairing_input_meta_data := mload(pcs_ptr) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(pairing_input_meta_data, 0xFF))) + pairing_input_meta_data := shr(8, pairing_input_meta_data) + let set_coeff := and(pairing_input_meta_data, 0xFFFF) + pairing_input_meta_data := shr(16, pairing_input_meta_data) + let ec_points_cptr_packed := and(pairing_input_meta_data, 0xFFFFFFFFFFFFFFFFFFFF) + pairing_input_meta_data := shr(80, pairing_input_meta_data) + let i := pcs_ptr + pcs_ptr := end_ptr_packed_lens + let first := 1 + for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { + for { } pairing_input_meta_data { } { + let len := and(pairing_input_meta_data, 0xFF) + pairing_input_meta_data := shr(8, pairing_input_meta_data) + if first { + first := 0 + success := pairing_input_computations_first(len, pcs_ptr, mload(pcs_ptr), theta_mptr, success) + pcs_ptr := add(pcs_ptr, len) + continue + } + success := pairing_input_computations(len, pcs_ptr, mload(pcs_ptr), theta_mptr, success) + pcs_ptr := add(pcs_ptr, len) + success := ec_mul_tmp(success, mulmod(nu, mload(set_coeff), R)) + set_coeff := add(set_coeff, 0x20) + success := ec_add_acc(success, mload(0x80), mload(0xa0)) + // execute this if statement if not the last set + if or(0x1, lt(i, sub(end_ptr_packed_lens, 0x20))) { + nu := mulmod(nu, mload(add(theta_mptr, 0xC0)), R) + } + } + pairing_input_meta_data := mload(add(i, 0x20)) + } + mstore(0x80, mload(add(vk_mptr, {{ vk_const_offsets["g1_x"]|hex() }}))) + mstore(0xa0, mload(add(vk_mptr, {{ vk_const_offsets["g1_y"]|hex() }}))) + success := ec_mul_tmp(success, sub(R, mload(add(theta_mptr, 0x2A0)))) + success := ec_add_acc(success, mload(0x80), mload(0xa0)) + mstore(0x80, calldataload(and(ec_points_cptr_packed, 0xFFFF))) + ec_points_cptr_packed := shr(16, ec_points_cptr_packed) + mstore(0xa0, calldataload(and(ec_points_cptr_packed, 0xFFFF))) + ec_points_cptr_packed := shr(16, ec_points_cptr_packed) + success := ec_mul_tmp(success, sub(R, mload(and(ec_points_cptr_packed, 0xFFFF)))) + ec_points_cptr_packed := shr(16, ec_points_cptr_packed) + success := ec_add_acc(success, mload(0x80), mload(0xa0)) + let w_prime_x := calldataload(and(ec_points_cptr_packed, 0xFFFF)) + ec_points_cptr_packed := shr(16, ec_points_cptr_packed) + let w_prime_y := calldataload(and(ec_points_cptr_packed, 0xFFFF)) + mstore(0x80, w_prime_x) + mstore(0xa0, w_prime_y) + success := ec_mul_tmp(success, mload(add(theta_mptr, 0xE0))) + success := ec_add_acc(success, mload(0x80), mload(0xa0)) + mstore(add(theta_mptr, 0x2C0), mload(0x00)) + mstore(add(theta_mptr, 0x2E0), mload(0x20)) + mstore(add(theta_mptr, 0x300), w_prime_x) + mstore(add(theta_mptr, 0x320), w_prime_y) } - {%- endfor %} } // Random linear combine with accumulator diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index b9efe67..e8a6e02 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -96,8 +96,14 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset_1 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] {%- endfor %} {%- let offset_2 = offset_1 + pcs_computations.r_evals_computations.len() %} - {%- for coeff_sum in pcs_computations.coeff_sums_computation %} - mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ coeff_sum|hex_padded(64) }}) // coeff_sums_computation[{{ loop.index0 }}] + {%- for coeff_sum_word in pcs_computations.coeff_sums_computation %} + mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ coeff_sum_word|hex_padded(64) }}) // coeff_sums_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_3 = offset_2 + pcs_computations.coeff_sums_computation.len() %} + mstore({{ (32 * offset_3)|hex_padded(4) }}, {{ pcs_computations.r_eval_computations|hex_padded(64) }}) // r_eval_computations + {%- let offset_4 = offset_3 + 1 %} + {%- for pairing_input_word in pcs_computations.pairing_input_computations %} + mstore({{ (32 * (offset_4 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] {%- endfor %} return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } From 7ceb2cc2ec7a23051769584216b3710ac66d58d5 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 5 Aug 2024 17:37:26 -0400 Subject: [PATCH 46/65] *pack challenges --- src/codegen.rs | 37 ++++++++++----- src/codegen/pcs.rs | 3 +- src/codegen/template.rs | 4 +- src/codegen/util.rs | 2 +- src/test.rs | 5 ++- templates/Halo2Verifier.sol | 2 +- templates/Halo2VerifierReusable.sol | 70 ++++++++++++++++++++--------- templates/Halo2VerifyingKey.sol | 31 ++++++------- 8 files changed, 98 insertions(+), 56 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 81b4b8a..4b8afd9 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -391,14 +391,31 @@ impl<'a> SolidityGenerator<'a> { .take(num_advices.len()) .copied() .collect::>(); - // Create a new vec of type of Vec with the values of num_advices and num_user_challenges. - let num_advices_user_challenges: Vec<(U256, U256)> = num_advices - .iter() - .zip(num_user_challenges.iter()) - .map(|(num_advices, num_user_challenges)| { - (U256::from(*num_advices), U256::from(*num_user_challenges)) - }) - .collect_vec(); + + let num_advices_user_challenges: Vec = { + let mut packed_words: Vec = vec![U256::from(0)]; + let mut bit_counter = 8; + let mut last_idx = 0; + for (num_advices, num_user_challenges) in + num_advices.iter().zip(num_user_challenges.iter()) + { + let offset = 24; + let next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + packed_words[last_idx] |= U256::from(*num_advices * 0x40) << bit_counter; + bit_counter += 16; + packed_words[last_idx] |= U256::from(*num_user_challenges) << bit_counter; + bit_counter += 8; + } + let packed_words_len = packed_words.len(); + // Encode the length of the exprs vec in the first word + packed_words[0] |= U256::from(packed_words_len); + packed_words + }; // Update constants @@ -408,8 +425,8 @@ impl<'a> SolidityGenerator<'a> { let num_advices_user_challenges_offset = (constants.len() * 0x20) + (fixed_comms.len() + permutation_comms.len()) * 0x40 + (const_expressions.len() * 0x20); - let gate_computations_len_offset = num_advices_user_challenges_offset - + ((num_advices_user_challenges.len() * 0x40) + 0x20); + let gate_computations_len_offset = + num_advices_user_challenges_offset + (num_advices_user_challenges.len() * 0x20); let permutations_computations_len_offset = gate_computations_len_offset + (0x20 * gate_computations_dummy.len()); let lookup_computations_len_offset = diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 82d9561..486cc46 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -937,8 +937,7 @@ pub(crate) fn bdfg21_computations_separate( // depending on if it is a single rotation set or not. // 3. Here is how the encoding of the single rotation set will look like: // 3a. After the 1 byte that contains the number of words with the packed ptr data for the given set, we encode the coeffs[0] ptr, - // the first eval ptr of the first group. TODO we need to assert that there is only one group of evals for a single rotation set, and that the eval in the second group index - // 1 is the quotient eval ptr. + // the first eval ptr of the first group. // 3b. Next we encode the number of eval ptrs in the eval_group, encoding the eval ptrs that follow until we reach the end of the eval_group, repeating the process for the next eval_group. // 4. Here is how the encoding of the not single rotation set will look like: // 4a. After the 1 byte that contains the number of words with the packed ptr data for the given set, the coeffs ptrs, diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 7c1ceab..18be850 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -16,7 +16,7 @@ use super::{ #[template(path = "Halo2VerifyingKey.sol")] pub(crate) struct Halo2VerifyingKey { pub(crate) constants: Vec<(&'static str, U256)>, - pub(crate) num_advices_user_challenges: Vec<(U256, U256)>, + pub(crate) num_advices_user_challenges: Vec, pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_expressions: Vec, @@ -31,7 +31,7 @@ impl Halo2VerifyingKey { (self.constants.len() * 0x20) + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 + (self.const_expressions.len() * 0x20) - + ((self.num_advices_user_challenges.len() * 0x40) + 0x20) + + (self.num_advices_user_challenges.len() * 0x20) + (self.gate_computations.len() * 0x20) + (self.permutation_computations.len() * 0x20) + (self.lookup_computations.len() * 0x20) diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 3bab77b..5177e70 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -401,7 +401,7 @@ impl Data { let challenge_mptr = permutation_comm_mptr + (2 * vk.permutation_comms.len()) + vk.const_expressions.len() - + (2 * vk.num_advices_user_challenges.len() + 1) + + vk.num_advices_user_challenges.len() + (vk.gate_computations.len()) + (vk.permutation_computations.len()) + (vk.lookup_computations.len()) diff --git a/src/test.rs b/src/test.rs index 5326f0c..be05848 100644 --- a/src/test.rs +++ b/src/test.rs @@ -100,9 +100,12 @@ fn run_render_separately>() { // // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); // // print vk_solidity - // println!("VK solidity: {vk_solidity}"); + println!("VK solidity: {vk_solidity}"); + // VK creation code size let vk_creation_code = compile_solidity(&vk_solidity); + let vk_creation_code_size = vk_creation_code.len(); + println!("VK creation code size: {vk_creation_code_size}"); let vk_address = evm.create(vk_creation_code); let (gas_cost, output) = evm.call( diff --git a/templates/Halo2Verifier.sol b/templates/Halo2Verifier.sol index 279a551..0cc10da 100644 --- a/templates/Halo2Verifier.sol +++ b/templates/Halo2Verifier.sol @@ -214,7 +214,7 @@ contract Halo2Verifier { // Modulus let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 // BN254 base field - let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field TODO: rmv this and replace with constant R + let r := 21888242871839275222246405745257275088548364400416034343698204186575808495617 // BN254 scalar field // Initialize success as true let success := true diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 3596712..2761b91 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -161,7 +161,8 @@ contract Halo2Verifier { ret1 := mload(computations_len_ptr) // Remember this length represented in bytes } - + // Returns the length of the SoA layout for the permutation z evaluations ptr, the permutation z evals ptr, + // the permutation chunk length and the first permutation z eval. function perm_comp_layout_metadata(offset, vk_mptr) -> ret0, ret1, ret2, ret3 { let computations_ptr, computations_len := soa_layout_metadata(offset, vk_mptr) let permutation_z_evals_ptr := add(computations_ptr, 0x20) @@ -640,29 +641,54 @@ contract Halo2Verifier { let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory // Set the theta_mptr (vk_mptr + vk_len + challenges_length) theta_mptr := add(challenge_mptr, mload(add(vk_mptr, {{ vk_const_offsets["challenges_offset"]|hex() }}))) - let num_advices_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) - let num_advices_len := mload(num_advices_ptr) - let advices_ptr := add(num_advices_ptr, 0x20) // start of advices - let challenges_ptr := add(advices_ptr, 0x20) // start of challenges - - // Iterate over phases using the loaded num_advices and num_challenges - for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 0x40) } { - // Calculate proof_cptr_end based on num_advices - let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, phase)))) // We use 0x40 because each advice is followed by the corresponding challenge - - // Phase loop - for { } lt(proof_cptr, proof_cptr_end) { } { - success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) - } - - // Generate challenges - challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) - - // Continue squeezing challenges based on num_challenges - for { let c := 1 } lt(c, mload(add(challenges_ptr, phase))) { c := add(c, 1) } { // We - challenge_mptr := squeeze_challenge_cont(challenge_mptr) + let challenge_len_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) + + let challenge_len_data := mload(challenge_len_ptr) + let num_words := and(challenge_len_data, 0xFF) + challenge_len_data := shr(8, challenge_len_data) + for { let i := 0 } lt(i, 1) { i := add(i, 1) } { + for { } challenge_len_data { } { + // add proof_cpt to num advices len + let proof_cptr_end := add(proof_cptr, and(challenge_len_data, 0xFFFF)) + challenge_len_data := shr(16, challenge_len_data) + // Phase loop + for { } lt(proof_cptr, proof_cptr_end) { } { + success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) + } + // Generate challenges + challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) + + // Continue squeezing challenges based on num_challenges + let num_challenges := and(challenge_len_data, 0xFF) + challenge_len_data := shr(8, challenge_len_data) + for { let c := 1 } lt(c, num_challenges) { c := add(c, 1) } { + challenge_mptr := squeeze_challenge_cont(challenge_mptr) + } } + challenge_len_ptr := add(challenge_len_ptr, 0x20) + challenge_len_data := mload(challenge_len_ptr) } + // let advices_ptr := add(num_advices_ptr, 0x20) // start of advices + // let challenges_ptr := add(advices_ptr, 0x20) // start of challenges + + // // Iterate over phases using the loaded num_advices and num_challenges + // for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 0x40) } { + // // Calculate proof_cptr_end based on num_advices + // let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, phase)))) // We use 0x40 because each advice is followed by the corresponding challenge + + // // Phase loop + // for { } lt(proof_cptr, proof_cptr_end) { } { + // success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) + // } + + // // Generate challenges + // challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) + + // // Continue squeezing challenges based on num_challenges + // for { let c := 1 } lt(c, mload(add(challenges_ptr, phase))) { c := add(c, 1) } { + // challenge_mptr := squeeze_challenge_cont(challenge_mptr) + // } + // } // Read evaluations for diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index e8a6e02..c483b51 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -22,26 +22,23 @@ contract Halo2VerifyingKey { {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} + {%- for word in num_advices_user_challenges %} {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{(2 * 32 * num_advices_user_challenges.len())|hex_padded(64) }}) // num_advices_user_challenges length - {%- for (x, y) in num_advices_user_challenges %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() + 1 %} - mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // num_advices[{{ loop.index0 }}].x - mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // user_challenges[{{ loop.index0 }}].y + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length {%- for packed_expression_word in gate_computations.packed_expression_words %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 1 %} {%- let offset = base_offset + loop.index0 %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ packed_expression_word|hex_padded(64) }}) // packed_expression_word [{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.z_evals_last_idx|hex_padded(64) }}) // z_evals_last_idx - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 1 + gate_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.chunk_offset|hex_padded(64) }}) // chunk_offset {%- for z_eval in permutation_computations.permutation_z_evals %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 3 + gate_computations.len() %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 2 + gate_computations.len() %} {%- let offset = base_offset + loop.index0 * (permutation_computations.column_evals[0].len() + 1)%} mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_evals[{{ loop.index0 }}] {%- let last_index = permutation_computations.permutation_z_evals.len() - 1 %} @@ -58,10 +55,10 @@ contract Halo2VerifyingKey { mstore({{ (32 * offset)|hex_padded(4) }}, {{ column_eval|hex_padded(64) }}) // column_eval[{{ loop.index0 }}] {%- endfor %} {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations {%- for lookup in lookup_computations.lookups %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 2 + gate_computations.len() + permutation_computations.len() %} + {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + 1 + const_expressions.len() + gate_computations.len() + permutation_computations.len() %} {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] {%- for table_line in lookup.table_lines %} @@ -77,19 +74,19 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() %} {%- for point_word in pcs_computations.point_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() %} {%- for vanishing_word in pcs_computations.vanishing_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() %} + {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() %} {%- for coeff_word in pcs_computations.coeff_computations %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset_0 = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} + {%- let offset_0 = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} mstore({{ (32 * offset_0)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations {%- let offset_1 = offset_0 + 1 %} {%- for r_eval_word in pcs_computations.r_evals_computations %} @@ -105,7 +102,7 @@ contract Halo2VerifyingKey { {%- for pairing_input_word in pcs_computations.pairing_input_computations %} mstore({{ (32 * (offset_4 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len() + num_advices_user_challenges.len()) + const_expressions.len() + 1 + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) + return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) } } } From 27f6052d59b4b04b05ba9f762e05efaea8357a56 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 7 Aug 2024 16:31:54 -0400 Subject: [PATCH 47/65] packed perm evaluations. --- src/codegen/evaluator.rs | 52 +++++++++++----- src/test.rs | 2 +- templates/Halo2VerifierReusable.sol | 82 ++++++++++++------------ templates/Halo2VerifyingKey.sol | 96 ++++++++++++----------------- 4 files changed, 116 insertions(+), 116 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 024d8b2..2935f3e 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -554,19 +554,15 @@ impl GateDataEncoded { // the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq)] pub struct PermutationDataEncoded { - pub(crate) z_evals_last_idx: U256, - pub(crate) chunk_offset: U256, - pub(crate) permutation_z_evals: Vec, - pub(crate) column_evals: Vec>, + pub(crate) permutation_meta_data: U256, + pub(crate) permutation_data: Vec, } impl Default for PermutationDataEncoded { fn default() -> Self { PermutationDataEncoded { - z_evals_last_idx: U256::from(0), - chunk_offset: U256::from(0), - permutation_z_evals: Vec::new(), - column_evals: Vec::new(), + permutation_meta_data: U256::from(0), + permutation_data: Vec::new(), } } } @@ -576,8 +572,7 @@ impl PermutationDataEncoded { if self == &Self::default() { 0 } else { - 3 + self.permutation_z_evals.len() - + self.column_evals.iter().map(Vec::len).sum::() + 1 + self.permutation_data.len() } } } @@ -707,8 +702,7 @@ where pub fn permutation_computations(&self) -> PermutationDataEncoded { let Self { meta, data, .. } = self; - let permutation_z_evals_last_idx = 32 * (data.permutation_z_evals.len() - 1); - let chunk_offset = meta.permutation_chunk_len + 1; + let permutation_z_evals_last_idx = data.permutation_z_evals.len() - 1; let permutation_z_evals: Vec = data .permutation_z_evals .iter() @@ -729,11 +723,37 @@ where .collect() }) .collect(); + // num words each set of permutation data will take up (except the last one) scaled by 0x20 + // 48 is the bit offset of the permutation_z_evals and 40 is the bit offset of each column eval. + let num_words = 1 + ((48 + (meta.permutation_chunk_len) * 40) / 256); + let perm_meta_data: U256 = { + let mut packed_word = U256::from(permutation_z_evals_last_idx); + packed_word |= U256::from(num_words * 0x20) << 8; + let last_num_words = 1 + ((48 + (column_evals.last().unwrap().len()) * 40) / 256); + packed_word |= U256::from(last_num_words * 0x20) << 24; + packed_word + }; + let perm_data: Vec = izip!(0.., column_evals) + .flat_map(|(chunk_idx, column_evals)| { + let mut packed_words = vec![permutation_z_evals[chunk_idx]]; + let mut last_idx = 0; + let mut bit_counter = 48; + for eval in column_evals.iter() { + let next_bit_counter = bit_counter + 40; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + packed_words[last_idx] |= *eval << bit_counter; + bit_counter += 40; + } + packed_words + }) + .collect_vec(); PermutationDataEncoded { - z_evals_last_idx: U256::from(permutation_z_evals_last_idx), - chunk_offset: U256::from(chunk_offset), - permutation_z_evals, - column_evals, + permutation_meta_data: perm_meta_data, + permutation_data: perm_data, } } diff --git a/src/test.rs b/src/test.rs index be05848..1dda749 100644 --- a/src/test.rs +++ b/src/test.rs @@ -100,7 +100,7 @@ fn run_render_separately>() { // // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); // // print vk_solidity - println!("VK solidity: {vk_solidity}"); + // println!("VK solidity: {vk_solidity}"); // VK creation code size let vk_creation_code = compile_solidity(&vk_solidity); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 2761b91..46cd6cb 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -174,7 +174,7 @@ contract Halo2Verifier { ret3 := permutation_z_evals } - function col_evals(z, chunk, permutation_z_evals_ptr, theta_mptr) { + function col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) { let gamma := mload(add(theta_mptr, 0x40)) let beta := mload(add(theta_mptr, 0x20)) let x := mload(add(theta_mptr, 0x80)) @@ -183,17 +183,21 @@ contract Halo2Verifier { let i_eval := mload(add(theta_mptr, 0x220)) // Extract the index 1 and index 0 z evaluations from the z word. let lhs := calldataload(and(shr(16,z), 0xFFFF)) - let rhs := calldataload(and(z, 0xFFFF)) + let rhs := calldataload(and(z, 0xFFFF)) + z := shr(48, z) // loop through the word_len_chunk - for { let j := 0x20 } lt(j, chunk) { j := add(j, 0x20) } { - let col_word := mload(add(permutation_z_evals_ptr, j)) - let eval := i_eval - if eq(and(col_word, 0xFF), 0x00) { - eval := calldataload(and(shr(8, col_word), 0xFFFF)) + for { let j := 0 } lt(j, num_words) { j := add(j, 0x20) } { + for { } z { } { + let eval := i_eval + if eq(and(z, 0xFF), 0x00) { + eval := calldataload(and(shr(8, z), 0xFFFF)) + } + lhs := mulmod(lhs, addmod(addmod(eval, mulmod(beta, calldataload(and(shr(24, z), 0xFFFF)), R), R), gamma, R), R) + rhs := mulmod(rhs, addmod(addmod(eval, mload(0x00), R), gamma, R), R) + z := shr(40, z) + mstore(0x00, mulmod(mload(0x00), DELTA, R)) } - lhs := mulmod(lhs, addmod(addmod(eval, mulmod(beta, calldataload(and(shr(24, col_word), 0xFFFF)), R), R), gamma, R), R) - rhs := mulmod(rhs, addmod(addmod(eval, mload(0x00), R), gamma, R), R) - mstore(0x00, mulmod(mload(0x00), DELTA, R)) + z := mload(add(permutation_z_evals_ptr, add(j, 0x20))) } let left_sub_right := addmod(lhs, sub(R, rhs), R) let fsm_ptr := mload(0x20) @@ -201,27 +205,27 @@ contract Halo2Verifier { mstore(0x20, add(fsm_ptr,0x20)) } - function z_evals(z, permutation_chunk, perm_z_last_ptr, permutation_z_evals_ptr, theta_mptr, l_0, y, quotient_eval_numer) -> ret { + function z_evals(z, num_words_packed, perm_z_last_ptr, permutation_z_evals_ptr, theta_mptr, l_0, y, quotient_eval_numer) -> ret { + let num_words := and(num_words_packed, 0xFFFF) // Initialize the free static memory pointer to store the column evals. mstore(0x20, 0x40) // Iterate through the tuple window length ( permutation_z_evals_len.len() - 1 ) offset by one word. for { } lt(permutation_z_evals_ptr, perm_z_last_ptr) { } { - let next_z_ptr := add(permutation_z_evals_ptr, permutation_chunk) + let next_z_ptr := add(permutation_z_evals_ptr, num_words) let z_j := mload(next_z_ptr) quotient_eval_numer := addmod( mulmod(quotient_eval_numer, y, R), mulmod(l_0, addmod(calldataload(and(z_j, 0xFFFF)), sub(R, calldataload(and(shr(32,z), 0xFFFF))), R), R), R ) - col_evals(z, permutation_chunk, permutation_z_evals_ptr, theta_mptr) + col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) permutation_z_evals_ptr := next_z_ptr z := z_j } // Due to the fact that permutation_columns.len() in H2 might not be divisble by permutation_chunk_len, the last column length might be less than permutation_chunk_len - // We store this length right after the last perm_z_evals word. - let chunk_offset_last_ptr := add(permutation_z_evals_ptr, 0x20) - permutation_chunk := mload(chunk_offset_last_ptr) // Remeber to store (columns.len() + 1) * 32 here - col_evals(z, permutation_chunk, chunk_offset_last_ptr, theta_mptr) + // We store this length in the last 16 bits of the num_words_packed word. + num_words := and(shr(16, num_words_packed), 0xFFFF) + col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) // iterate through col_evals to update the quotient_eval_numer accumulator let end_ptr := mload(0x20) for { let j := 0x40 } lt(j, end_ptr) { j := add(j, 0x20) } { @@ -668,27 +672,6 @@ contract Halo2Verifier { challenge_len_ptr := add(challenge_len_ptr, 0x20) challenge_len_data := mload(challenge_len_ptr) } - // let advices_ptr := add(num_advices_ptr, 0x20) // start of advices - // let challenges_ptr := add(advices_ptr, 0x20) // start of challenges - - // // Iterate over phases using the loaded num_advices and num_challenges - // for { let phase := 0 } lt(phase, num_advices_len) { phase := add(phase, 0x40) } { - // // Calculate proof_cptr_end based on num_advices - // let proof_cptr_end := add(proof_cptr, mul(0x40, mload(add(advices_ptr, phase)))) // We use 0x40 because each advice is followed by the corresponding challenge - - // // Phase loop - // for { } lt(proof_cptr, proof_cptr_end) { } { - // success, proof_cptr, hash_mptr := read_ec_point(success, proof_cptr, hash_mptr) - // } - - // // Generate challenges - // challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) - - // // Continue squeezing challenges based on num_challenges - // for { let c := 1 } lt(c, mload(add(challenges_ptr, phase))) { c := add(c, 1) } { - // challenge_mptr := squeeze_challenge_cont(challenge_mptr) - // } - // } // Read evaluations for @@ -856,7 +839,7 @@ contract Halo2Verifier { let y := mload(add(theta_mptr, 0x60)) { // Gate computations / expression evaluations. - let computations_ptr, computations_len := soa_layout_metadata(0x380, vk_mptr) + let computations_ptr, computations_len := soa_layout_metadata({{ vk_const_offsets["gate_computations_len_offset"]|hex() }}, vk_mptr) let expressions_word := mload(computations_ptr) let last_idx // Load in the total number of code blocks from the vk constants, right after the number challenges @@ -878,7 +861,18 @@ contract Halo2Verifier { } { // Permutation computations - let computations_len, permutation_z_evals_ptr, permutation_chunk, permutation_z_evals := perm_comp_layout_metadata(0x3a0, vk_mptr) + let permutation_z_evals_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["permutation_computations_len_offset"]|hex() }}))) + let permutation_z_evals := mload(permutation_z_evals_ptr) + // Last idx of permutation evals == permutation_evals.len() - 1 + let last_idx := and(permutation_z_evals, 0xFF) + permutation_z_evals := shr(8, permutation_z_evals) + // Num of words scaled by 0x20 that take up each permutation eval (permutation_z_eval + column evals) + // first and second LSG bytes contain the number of words for all of the permutation evals except the last + // the third and fourth LSG bytes contain the number of words for the last permutation eval + let num_words := and(permutation_z_evals, 0xFFFFFFFF) + permutation_z_evals := shr(32, permutation_z_evals) + permutation_z_evals_ptr := add(permutation_z_evals_ptr, 0x20) + permutation_z_evals := mload(permutation_z_evals_ptr) let l_0 := mload(add(theta_mptr, 0x200)) { // Get the first and second LSG bytes from the first permutation_z_evals word to load in (z, _, _) @@ -888,7 +882,7 @@ contract Halo2Verifier { { // Load in the last permutation_z_evals word - let perm_z_last_ptr := add(mul(computations_len, permutation_chunk), permutation_z_evals_ptr) + let perm_z_last_ptr := add(mul(last_idx, and(num_words, 0xFFFF)), permutation_z_evals_ptr) let perm_z_last := calldataload(and(mload(perm_z_last_ptr), 0xFFFF)) quotient_eval_numer := addmod( mulmod(quotient_eval_numer, y, R), @@ -909,7 +903,7 @@ contract Halo2Verifier { quotient_eval_numer := z_evals( permutation_z_evals, // Update the chunk offset to be in bytes - mul(0x20, permutation_chunk), + num_words, perm_z_last_ptr, permutation_z_evals_ptr, theta_mptr, @@ -926,7 +920,9 @@ contract Halo2Verifier { mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind mstore(0x60, mload(theta_mptr)) // theta mstore(0x80, mload(add(theta_mptr, 0x20))) // beta - let evals_ptr, end_ptr := soa_layout_metadata(0x3c0, vk_mptr) + let evals_ptr, end_ptr := soa_layout_metadata({{ + vk_const_offsets["lookup_computations_len_offset"]|hex() + }}, vk_mptr) if end_ptr { // iterate through the input_tables_len for { } lt(evals_ptr, end_ptr) { } { diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index c483b51..afb110e 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -8,57 +8,41 @@ contract Halo2VerifyingKey { {%- for (name, chunk) in constants %} mstore({{ (32 * loop.index0)|hex_padded(4) }}, {{ chunk|hex_padded(64) }}) // {{ name }} {%- endfor %} + {%- let offset_0 = constants.len() %} {%- for (x, y) in fixed_comms %} - {%- let offset = constants.len() %} - mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x - mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y + mstore({{ (32 * (offset_0 + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x + mstore({{ (32 * (offset_0 + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y {%- endfor %} + {%- let offset_1 = offset_0 + 2 * fixed_comms.len() %} {%- for (x, y) in permutation_comms %} - {%- let offset = constants.len() + 2 * fixed_comms.len() %} - mstore({{ (32 * (offset + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x - mstore({{ (32 * (offset + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y + mstore({{ (32 * (offset_1 + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x + mstore({{ (32 * (offset_1 + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} + {%- let offset_2 = offset_1 + 2 * permutation_comms.len() %} {%- for const in const_expressions %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] + mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} + {%- let offset_3 = offset_2 + const_expressions.len() %} {%- for word in num_advices_user_challenges %} - {%- let offset = constants.len() + 2 * fixed_comms.len() + 2 * permutation_comms.len() + const_expressions.len() %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] + mstore({{ (32 * (offset_3 + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length + {%- let offset_4 = offset_3 + num_advices_user_challenges.len() %} + mstore({{ (32 * offset_4)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length + {%- let offset_5 = offset_4 + 1 %} {%- for packed_expression_word in gate_computations.packed_expression_words %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 1 %} - {%- let offset = base_offset + loop.index0 %} + {%- let offset = offset_5 + loop.index0 %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ packed_expression_word|hex_padded(64) }}) // packed_expression_word [{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.z_evals_last_idx|hex_padded(64) }}) // z_evals_last_idx - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 1 + gate_computations.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ permutation_computations.chunk_offset|hex_padded(64) }}) // chunk_offset - {%- for z_eval in permutation_computations.permutation_z_evals %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + 2 + gate_computations.len() %} - {%- let offset = base_offset + loop.index0 * (permutation_computations.column_evals[0].len() + 1)%} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ z_eval|hex_padded(64) }}) // permutation_z_evals[{{ loop.index0 }}] - {%- let last_index = permutation_computations.permutation_z_evals.len() - 1 %} - {%- let plus_one %} - {%- if loop.index0 == last_index %} - {%- let offset = offset + 1 %} - {%- let plus_one = 1 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ (32 * (permutation_computations.column_evals[last_index].len() + 1))|hex_padded(64) }}) // chunk_offset_last - {%- else -%} - {%- let plus_one = 0 -%} - {%- endif %} - {%- for column_eval in permutation_computations.column_evals[loop.index0] %} - {%- let offset = offset + loop.index0 + 1 + plus_one %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ column_eval|hex_padded(64) }}) // column_eval[{{ loop.index0 }}] - {%- endfor %} - {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations + {%- let offset_6 = offset_4 + gate_computations.len() %} + mstore({{ (32 * offset_6)|hex_padded(4) }}, {{ permutation_computations.permutation_meta_data|hex_padded(64) }}) // permutation_meta_data + {%- for word in permutation_computations.permutation_data %} + {%- let offset = offset_6 + 1 + loop.index0 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ word|hex_padded(64) }}) // permutation_data [{{ loop.index0 }}] + {%- endfor %} + {%- let offset_7 = offset_6 + permutation_computations.len() %} + mstore({{ (32 * offset_7)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations + {%- let base_offset = offset_7 + 1 %} {%- for lookup in lookup_computations.lookups %} - {%- let base_offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + 1 + const_expressions.len() + gate_computations.len() + permutation_computations.len() %} {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] {%- for table_line in lookup.table_lines %} @@ -74,35 +58,35 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() %} + {%- let offset_8 = offset_7 + lookup_computations.len() %} {%- for point_word in pcs_computations.point_computations %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_8 + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() %} + {%- let offset_9 = offset_8 + pcs_computations.point_computations.len() %} {%- for vanishing_word in pcs_computations.vanishing_computations %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_9 + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() %} + {%- let offset_10 = offset_9 + pcs_computations.vanishing_computations.len() %} {%- for coeff_word in pcs_computations.coeff_computations %} - mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_10 + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset_0 = constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.point_computations.len() + pcs_computations.vanishing_computations.len() + pcs_computations.coeff_computations.len() %} - mstore({{ (32 * offset_0)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations - {%- let offset_1 = offset_0 + 1 %} + {%- let offset_11 = offset_10 + pcs_computations.coeff_computations.len() %} + mstore({{ (32 * offset_11)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations + {%- let offset_12 = offset_11 + 1 %} {%- for r_eval_word in pcs_computations.r_evals_computations %} - mstore({{ (32 * (offset_1 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_12 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset_2 = offset_1 + pcs_computations.r_evals_computations.len() %} + {%- let offset_13 = offset_12 + pcs_computations.r_evals_computations.len() %} {%- for coeff_sum_word in pcs_computations.coeff_sums_computation %} - mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ coeff_sum_word|hex_padded(64) }}) // coeff_sums_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_13 + loop.index0))|hex_padded(4) }}, {{ coeff_sum_word|hex_padded(64) }}) // coeff_sums_computations[{{ loop.index0 }}] {%- endfor %} - {%- let offset_3 = offset_2 + pcs_computations.coeff_sums_computation.len() %} - mstore({{ (32 * offset_3)|hex_padded(4) }}, {{ pcs_computations.r_eval_computations|hex_padded(64) }}) // r_eval_computations - {%- let offset_4 = offset_3 + 1 %} + {%- let offset_14 = offset_13 + pcs_computations.coeff_sums_computation.len() %} + mstore({{ (32 * offset_14)|hex_padded(4) }}, {{ pcs_computations.r_eval_computations|hex_padded(64) }}) // r_eval_computations + {%- let offset_15 = offset_14 + 1 %} {%- for pairing_input_word in pcs_computations.pairing_input_computations %} - mstore({{ (32 * (offset_4 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] + mstore({{ (32 * (offset_15 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] {%- endfor %} - return(0, {{ (32 * (constants.len() + 2 * (fixed_comms.len() + permutation_comms.len()) + num_advices_user_challenges.len() + const_expressions.len() + gate_computations.len() + permutation_computations.len() + lookup_computations.len() + pcs_computations.len()))|hex() }}) + return(0, {{ (32 * (offset_8 + pcs_computations.len()))|hex() }}) } } } From 18ed00ae867c6234837471a3eedbf53d1d77f2d9 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 8 Aug 2024 13:52:47 -0400 Subject: [PATCH 48/65] hardcode coeff_ptr --- src/codegen/pcs.rs | 28 +++++--------------------- templates/Halo2VerifierReusable.sol | 31 ++++++++++++----------------- 2 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 486cc46..fc2dcbc 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -956,10 +956,10 @@ pub(crate) fn bdfg21_computations_separate( *single_rot_set <= 1, "Only one single rotation set in the r_evals_computations" ); - coeff_len + coeff_len * 32 } else { assert!(coeff_len != 0, "The number of rotations in a set is 0"); - coeff_len * 16 + coeff_len * 32 } }; @@ -1038,7 +1038,6 @@ pub(crate) fn bdfg21_computations_separate( let process_single_rotation_set = |set: &RotationSet, - coeffs: &Vec, packed_words: &mut Vec, bit_counter: &mut usize, last_idx: &mut usize| { @@ -1062,18 +1061,11 @@ pub(crate) fn bdfg21_computations_separate( }, ); - let coeff_ptr = coeffs[0].ptr(); let first_eval_ptr = eval_groups[0][0].ptr(); assert!( eval_groups[1][0].ptr().loc() == Location::Memory, "The second eval group for a single rotation set should be memory but it is not" ); - - pack_value( - &mut packed_words[0], - coeff_ptr.value().as_usize(), - bit_counter, - ); pack_value( &mut packed_words[0], first_eval_ptr.value().as_usize(), @@ -1098,18 +1090,9 @@ pub(crate) fn bdfg21_computations_separate( let process_multiple_rotation_set = |set: &RotationSet, - coeffs: &Vec, packed_words: &mut Vec, bit_counter: &mut usize, last_idx: &mut usize| { - for coeff in coeffs.iter() { - pack_value( - &mut packed_words[0], - coeff.ptr().value().as_usize(), - bit_counter, - ); - } - for evals in set.evals().iter().rev() { let offset = coeffs.len() * 16; let next_bit_counter = *bit_counter + offset; @@ -1128,8 +1111,9 @@ pub(crate) fn bdfg21_computations_separate( } }; - let r_evals_data: Vec = izip!(&sets, &coeffs) - .flat_map(|(set, coeffs)| { + let r_evals_data: Vec = sets + .iter() + .flat_map(|set| { let mut packed_words = vec![U256::from(0)]; let mut last_idx = 0; let mut bit_counter = 8; @@ -1137,7 +1121,6 @@ pub(crate) fn bdfg21_computations_separate( if set.rots().len() == 1 { process_single_rotation_set( set, - coeffs, &mut packed_words, &mut bit_counter, &mut last_idx, @@ -1145,7 +1128,6 @@ pub(crate) fn bdfg21_computations_separate( } else { process_multiple_rotation_set( set, - coeffs, &mut packed_words, &mut bit_counter, &mut last_idx, diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 46cd6cb..a511a41 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -372,9 +372,8 @@ contract Halo2Verifier { } } - function single_rot_set(r_evals_data, ptr, num_words, zeta, quotient_eval) -> ret0, ret1 { - let coeff := mload(and(r_evals_data, 0xFFFF)) - r_evals_data := shr(16, r_evals_data) + function single_rot_set(r_evals_data, ptr, num_words, zeta, quotient_eval, coeff_ptr) -> ret0, ret1 { + let coeff := mload(coeff_ptr) let r_eval r_eval := addmod(r_eval, mulmod(coeff, calldataload(and(r_evals_data, 0xFFFF)), R), R) r_evals_data := shr(16, r_evals_data) @@ -412,14 +411,12 @@ contract Halo2Verifier { ret1 := ptr } - function multi_rot_set(r_evals_data, ptr, num_words, rot_len, zeta) -> ret0, ret1 { - let coeffs_ptrs := and(r_evals_data, sub(shl(rot_len, 1), 1)) - r_evals_data := shr(rot_len, r_evals_data) + function multi_rot_set(r_evals_data, ptr, num_words, rot_len, zeta, coeff_ptr) -> ret0, ret1 { let r_eval := 0 for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { for { } r_evals_data { } { - for { let j := 0 } lt(j, rot_len) { j := add(j, 16) } { - r_eval := addmod(r_eval, mulmod(mload(and(shr(j, coeffs_ptrs), 0xFFFF)), calldataload(and(r_evals_data, 0xFFFF)), R), R) + for { let j := 0 } lt(j, rot_len) { j := add(j, 0x20) } { + r_eval := addmod(r_eval, mulmod(mload(add(coeff_ptr, j)), calldataload(and(r_evals_data, 0xFFFF)), R), R) r_evals_data := shr(16, r_evals_data) } // Only on the last index do we NOT execute this if block. @@ -434,16 +431,16 @@ contract Halo2Verifier { ret1 := ptr } - function r_evals_computation(rot_len, r_evals_data_ptr, zeta, quotient_eval) -> ret0, ret1 { + function r_evals_computation(rot_len, r_evals_data_ptr, zeta, quotient_eval, coeff_ptr) -> ret0, ret1 { let r_evals_data := mload(r_evals_data_ptr) // number of words to encode the data needed for this set in the r_evals computation. let num_words := and(r_evals_data, 0xFF) r_evals_data := shr(8, r_evals_data) switch rot_len - case 0x1 { - ret0, ret1 := single_rot_set(r_evals_data, r_evals_data_ptr, num_words, zeta, quotient_eval) + case 0x20 { + ret0, ret1 := single_rot_set(r_evals_data, r_evals_data_ptr, num_words, zeta, quotient_eval, coeff_ptr) } default { - ret0, ret1 := multi_rot_set(r_evals_data, r_evals_data_ptr, num_words, rot_len, zeta) + ret0, ret1 := multi_rot_set(r_evals_data, r_evals_data_ptr, num_words, rot_len, zeta, coeff_ptr) } } @@ -962,7 +959,6 @@ contract Halo2Verifier { // iterate through the outer_inputs_len let last_idx := sub(outer_inputs_len, 0x20) for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { - // iterate through the outer_inputs_len let tmp := mload(0xa0) if eq(i, 0){ tmp := mload(0xc0) @@ -1152,9 +1148,8 @@ contract Halo2Verifier { } pcs_ptr := add(pcs_ptr, 0x20) } + let coeff_ptr := 0x20 // r_evals_computations - // TODO optimizize this by hardcoding the coeff_ptr and iterating it - // over the coeff_len_data. Will reduce VK size. { let r_evals_meta_data := mload(pcs_ptr) let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(r_evals_meta_data, 0xFF))) @@ -1171,8 +1166,8 @@ contract Halo2Verifier { let r_eval for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { for { } r_evals_meta_data { } { - // pass the is_single_rot_set: bool, r_evals_data word, - r_eval, pcs_ptr := r_evals_computation(and(r_evals_meta_data, 0xFF), pcs_ptr, zeta, quotient_eval) + r_eval, pcs_ptr := r_evals_computation(and(r_evals_meta_data, 0xFF), pcs_ptr, zeta, quotient_eval, coeff_ptr) + coeff_ptr := add(coeff_ptr, and(r_evals_meta_data, 0xFF)) r_evals_meta_data := shr(8, r_evals_meta_data) if not_first { r_eval := mulmod(r_eval, mload(set_coeff), R) @@ -1190,7 +1185,7 @@ contract Halo2Verifier { let coeff_sums_data := mload(pcs_ptr) let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_sums_data, 0xFF))) coeff_sums_data := shr(8, coeff_sums_data) - let coeff_ptr := 0x20 + coeff_ptr := 0x20 let i := pcs_ptr pcs_ptr := end_ptr_packed_lens for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { From 1d60e0c66c7a63dac5f6efdde4c7b36b214bf180 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 9 Aug 2024 17:33:02 -0400 Subject: [PATCH 49/65] *MV lookup packed --- examples/separately.rs | 4 +- src/codegen/evaluator.rs | 129 ++++++++++++++++++---------- src/evm.rs | 6 +- src/test.rs | 12 ++- templates/Halo2VerifierReusable.sol | 48 +++++------ templates/Halo2VerifyingKey.sol | 16 ++-- 6 files changed, 133 insertions(+), 82 deletions(-) diff --git a/examples/separately.rs b/examples/separately.rs index 89c3ed8..52a54eb 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -24,7 +24,7 @@ fn main() { println!("Verifier creation code size: {verifier_creation_code_size}"); let mut evm = Evm::default(); - let verifier_address = evm.create(verifier_creation_code); + let (verifier_address, _) = evm.create(verifier_creation_code); let deployed_verifier_solidity = verifier_solidity; @@ -41,7 +41,7 @@ fn main() { assert_eq!(deployed_verifier_solidity, verifier_solidity); let vk_creation_code = compile_solidity(&vk_solidity); - let vk_address = evm.create(vk_creation_code); + let (vk_address, _) = evm.create(vk_creation_code); let calldata = { let instances = circuit.instances(); diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 2935f3e..9d0640c 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -11,6 +11,7 @@ use halo2_proofs::{ use itertools::{chain, izip, Itertools}; use regex::Regex; use ruint::aliases::U256; +use ruint::Uint; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; use super::util::{get_memory_ptr, Ptr, Word}; @@ -591,7 +592,7 @@ pub struct InputsEncoded { pub struct LookupEncoded { pub(crate) evals: U256, pub(crate) table_lines: Vec, - pub(crate) table_inputs: U256, + pub(crate) table_inputs: Option, pub(crate) acc: usize, pub(crate) inputs: Vec, } @@ -608,7 +609,13 @@ pub struct LookupEncoded { // sum of the lengths of the inputs. impl LookupEncoded { pub fn len(&self) -> usize { - 3 + (self.inputs.len()) + let base_len = if self.table_inputs.is_none() { + 1 // Add 2 if table_inputs is none + } else { + 2 // Add 3 otherwise + }; + base_len + + (self.inputs.len()) + self .inputs .iter() @@ -623,14 +630,14 @@ impl LookupEncoded { // needed in the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq)] pub struct LookupsDataEncoded { - pub(crate) end_ptr: U256, + pub(crate) meta_data: U256, pub(crate) lookups: Vec, } impl Default for LookupsDataEncoded { fn default() -> Self { LookupsDataEncoded { - end_ptr: U256::from(0), + meta_data: U256::from(0), lookups: Vec::new(), } } @@ -684,6 +691,7 @@ where } } + #[allow(dead_code)] pub fn gate_computation_fsm_usage(&self) -> usize { let packed_expression_words: Vec> = self .cs @@ -759,19 +767,22 @@ where #[cfg(not(feature = "mv-lookup"))] pub fn quotient_eval_fsm_usage(&self) -> usize { - let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); - - let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; - - // TODO implement the non mv lookup version of this calculation. - let input_expressions_fsm_usage = 0; - - itertools::max([ - gate_computation_fsm_usage, - permutation_computation_fsm_usage, - input_expressions_fsm_usage, - ]) - .unwrap() + unimplemented!( + "quotient_eval_fsm_usage function is not implemented for the non mv-lookup version of the verifier" + ); + // let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); + + // let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; + + // // TODO implement the non mv lookup version of this calculation. + // let input_expressions_fsm_usage = 0; + + // itertools::max([ + // gate_computation_fsm_usage, + // permutation_computation_fsm_usage, + // input_expressions_fsm_usage, + // ]) + // .unwrap() } #[cfg(feature = "mv-lookup")] @@ -835,7 +846,7 @@ where }); assert!(inputs.len() <= 16); self.reset(); - let lines_packed = self.encode_pack_expr_operations(lines); + let lines_packed = self.encode_pack_expr_operations(lines, 8); (lines_packed, inputs) }; @@ -854,7 +865,9 @@ where acc }); self.reset(); - let lines_packed = self.encode_pack_expr_operations(lines); + // bit offset to store the number of inputs + let bit_offset = if idx == 0 { 24 } else { 8 }; + let lines_packed = self.encode_pack_expr_operations(lines, bit_offset); (lines_packed, inputs) }; @@ -864,10 +877,15 @@ where .iter() .map(|lookup| { let inputs_iter = lookup.input_expressions().iter().enumerate(); + // outer inputs of the MV lookup vector scaled by 0x20. + let outer_inputs_len = lookup.input_expressions().len() * 0x20; let inputs = inputs_iter .clone() .map(|(idx, expressions)| { - let (lines, inputs) = evaluate_inputs(idx, expressions); + let (mut lines, inputs) = evaluate_inputs(idx, expressions); + if idx == 0 { + lines[0] |= U256::from(outer_inputs_len); + } (lines, inputs) }) .collect_vec(); @@ -878,11 +896,22 @@ where let mut accumulator = 0; + let mut previous_table_lines: Option>> = None; + + // Ensure that the number of inputs tables is less than 30 otherwise we won't be able to + // pack all of the shared input table expressions into the meta data. + assert!(inputs_tables.len() <= 30); + + // meta_data will encode the subsequence_indices as a bitmask, where each byte will store + // whether we use the previous table lines or not. If we use the previous table lines then + // the byte will be 0x0 otherwise it will be 0x1. + let mut meta_data = U256::from(0); let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) - .map(|(inputs_tables, evals)| { + .enumerate() // Add enumeration to track indices + .map(|(index, (inputs_tables, evals))| { let (inputs, (table_lines, table_inputs)) = inputs_tables.clone(); let evals = self.encode_triplet_evaluation_word(evals); - let table_inputs = self.encode_pack_ptrs(&table_inputs).unwrap(); + let table_inputs = Some(self.encode_pack_ptrs(&table_inputs).unwrap()); let mut inner_accumulator = 0; let inputs: Vec = inputs .iter() @@ -897,37 +926,50 @@ where res }) .collect_vec(); - let lookup_encoded = LookupEncoded { + + let mut lookup_encoded = LookupEncoded { evals, table_lines: table_lines.clone(), table_inputs, inputs: inputs.clone(), acc: accumulator, }; - accumulator += inputs - .iter() - .map(|inputs| inputs.expression.len()) - .sum::() - + (inputs.len() * 2); + + // bit offset to store the end_ptr + let offset = 16; + + // Handle subsequence indexing logic + if let Some(prev_lines) = &previous_table_lines { + if *prev_lines != table_lines { + meta_data |= U256::from(0x1) << ((index * 8) + offset); + } else { + lookup_encoded.table_lines = Vec::new(); + lookup_encoded.table_inputs = None; + } + } else { + meta_data |= U256::from(0x1) << ((index * 8) + offset); + } + + accumulator += lookup_encoded.len(); + + previous_table_lines = Some(table_lines); lookup_encoded }) .collect_vec(); - let mut data = LookupsDataEncoded { - lookups, - end_ptr: U256::from(0), - }; - if data.lookups.is_empty() { - data.end_ptr = U256::from(0x0); - return data; - } - data.end_ptr = U256::from((data.len() * 32) + offset); + + let mut data = LookupsDataEncoded { lookups, meta_data }; + // Insert the end_ptr to the beginning of the meta data word. + data.meta_data |= U256::from((data.len() * 32) + offset); data } #[cfg(not(feature = "mv-lookup"))] pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { - // TODO implement non mv lookup version of this - LookupsDataEncoded::default() + unimplemented!( + "Lookup_computations function is not implemented for the non mv-lookup version of the verifier" + ); + // // TODO implement non mv lookup version of this + // LookupsDataEncoded::default() } fn eval_encoded( @@ -996,10 +1038,10 @@ where Ok(packed) } - fn encode_pack_expr_operations(&self, exprs: Vec) -> Vec { + fn encode_pack_expr_operations(&self, exprs: Vec, mut bit_counter: i32) -> Vec { let mut packed_words: Vec = vec![U256::from(0)]; - let mut bit_counter = 8; let mut last_idx = 0; + let initial_offset = bit_counter; for expr in exprs.iter() { let first_byte = expr.as_limbs()[0] & 0xFF; @@ -1024,7 +1066,8 @@ where let packed_words_len = packed_words.len(); // Encode the length of the exprs vec in the first word - packed_words[0] |= U256::from(packed_words_len); + let offset = if initial_offset == 24 { 16 } else { 0 }; + packed_words[0] |= U256::from(packed_words_len) << offset; packed_words } @@ -1035,7 +1078,7 @@ where self.reset(); let res = result.0; if pack { - self.encode_pack_expr_operations(res) + self.encode_pack_expr_operations(res, 8) } else { res } diff --git a/src/evm.rs b/src/evm.rs index 851a5a2..fe9fcc0 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -167,15 +167,15 @@ pub(crate) mod test { /// /// # Panics /// Panics if execution reverts or halts unexpectedly. - pub fn create(&mut self, bytecode: Vec) -> Address { - let (_, output) = self.transact_success_or_panic(TxEnv { + pub fn create(&mut self, bytecode: Vec) -> (Address, u64) { + let (gas_used, output) = self.transact_success_or_panic(TxEnv { gas_limit: u64::MAX, transact_to: TransactTo::Create(CreateScheme::Create), data: bytecode.into(), ..Default::default() }); match output { - Output::Create(_, Some(address)) => address, + Output::Create(_, Some(address)) => (address, gas_used), _ => unreachable!(), } } diff --git a/src/test.rs b/src/test.rs index 1dda749..1189e96 100644 --- a/src/test.rs +++ b/src/test.rs @@ -58,11 +58,12 @@ fn run_render>() { let verifier_creation_code_size = verifier_creation_code.len(); let mut evm = Evm::unlimited(); - let verifier_address = evm.create(verifier_creation_code); + let (verifier_address, gas_cost) = evm.create(verifier_creation_code); let verifier_runtime_code_size = evm.code_size(verifier_address); println!("Verifier creation code size: {verifier_creation_code_size}"); println!("Verifier runtime code size: {verifier_runtime_code_size}"); + println!("Gas deployment cost verifier: {gas_cost}"); let (gas_cost, output) = evm.call(verifier_address, encode_calldata(None, &proof, &instances)); assert_eq!(output, [vec![0; 31], vec![1]].concat()); @@ -81,7 +82,7 @@ fn run_render_separately>() { let verifier_creation_code_size = verifier_creation_code.len(); let mut evm = Evm::unlimited(); - let verifier_address = evm.create(verifier_creation_code); + let (verifier_address, _gas_cost) = evm.create(verifier_creation_code); let verifier_runtime_code_size = evm.code_size(verifier_address); println!("Verifier creation code size: {verifier_creation_code_size}"); @@ -99,14 +100,17 @@ fn run_render_separately>() { assert_eq!(deployed_verifier_solidity, verifier_solidity); // // print verifier_solidity // println!("Verifier solidity: {verifier_solidity}"); - // // print vk_solidity + // print vk_solidity // println!("VK solidity: {vk_solidity}"); // VK creation code size let vk_creation_code = compile_solidity(&vk_solidity); let vk_creation_code_size = vk_creation_code.len(); println!("VK creation code size: {vk_creation_code_size}"); - let vk_address = evm.create(vk_creation_code); + let (vk_address, gas_cost) = evm.create(vk_creation_code); + let vk_runtime_code_size = evm.code_size(vk_address); + println!("VK runtime code size: {vk_runtime_code_size}"); + println!("Gas deployment cost VK: {gas_cost}"); let (gas_cost, output) = evm.call( verifier_address, diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index a511a41..3b5a0e2 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -161,19 +161,6 @@ contract Halo2Verifier { ret1 := mload(computations_len_ptr) // Remember this length represented in bytes } - // Returns the length of the SoA layout for the permutation z evaluations ptr, the permutation z evals ptr, - // the permutation chunk length and the first permutation z eval. - function perm_comp_layout_metadata(offset, vk_mptr) -> ret0, ret1, ret2, ret3 { - let computations_ptr, computations_len := soa_layout_metadata(offset, vk_mptr) - let permutation_z_evals_ptr := add(computations_ptr, 0x20) - let permutation_chunk := mload(computations_ptr) // Don't multiply by 0x20 word size here. Just encode permutation_chunk_len + 1 - let permutation_z_evals := mload(permutation_z_evals_ptr) - ret0 := computations_len - ret1 := permutation_z_evals_ptr - ret2 := permutation_chunk - ret3 := permutation_z_evals - } - function col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) { let gamma := mload(add(theta_mptr, 0x40)) let beta := mload(add(theta_mptr, 0x20)) @@ -278,11 +265,11 @@ contract Halo2Verifier { function expression_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { // Load in the least significant byte of the `expressions_word` word to get the total number of words we will need to load in. - let num_words := add(mul(0x20, and(expressions_word, 0xFF)), 0x20) + let num_words_shift_up_one := add(mul(0x20, and(expressions_word, 0xFF)), 0x20) // start of the expression encodings expressions_word := shr(8, expressions_word) let acc - for { let i := 0x20 } lt(i, num_words) { i := add(i, 0x20) } { + for { let i := 0x20 } lt(i, num_words_shift_up_one) { i := add(i, 0x20) } { for { } expressions_word { } { expressions_word := expression_operations(expressions_word, fsmp, acc) acc := add(acc, 0x20) @@ -917,10 +904,15 @@ contract Halo2Verifier { mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind mstore(0x60, mload(theta_mptr)) // theta mstore(0x80, mload(add(theta_mptr, 0x20))) // beta - let evals_ptr, end_ptr := soa_layout_metadata({{ + let evals_ptr, lookup_meta_data := soa_layout_metadata({{ vk_const_offsets["lookup_computations_len_offset"]|hex() }}, vk_mptr) - if end_ptr { + // lookup meta data contains 32 byte flags for indicating if we need to do a lookup table lines + // expression evaluation or we can use the previous one cached in the table var. + if lookup_meta_data { + let table + let end_ptr := and(lookup_meta_data, 0xFFFF) + lookup_meta_data := shr(16, lookup_meta_data) // iterate through the input_tables_len for { } lt(evals_ptr, end_ptr) { } { let evals := mload(evals_ptr) @@ -935,21 +927,27 @@ contract Halo2Verifier { mulmod(mload(0x00), calldataload(phi), R), R ) - let table - // load in the table_lines_len from the evals_ptr + // load in the lookup_table_lines from the evals_ptr evals_ptr := add(evals_ptr, 0x20) - evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - let outer_inputs_len := mload(evals_ptr) - for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { + // Due to the fact that lookups share the previous table, we can cache the previous table. + if and(lookup_meta_data, 0xFF) { + evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) evals_ptr := add(evals_ptr, 0x20) + } + lookup_meta_data := shr(8, lookup_meta_data) + let input_expression := mload(evals_ptr) + // outer inputs len, stored in the first input expression word, shifted up by the free static memory offset of 0xa0 + let outer_inputs_len := and(input_expression, 0xFFFF) + input_expression := shr(16, input_expression) + for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { // call the expression_evals function to evaluate the input_lines let ident - evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) + evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression, mload(0x60), mload(0x80)) + evals_ptr := add(evals_ptr, 0x20) + input_expression := mload(evals_ptr) // store ident in free static memory mstore(j, ident) } - evals_ptr := add(evals_ptr, 0x20) let lhs let rhs switch eq(outer_inputs_len, 0x20) diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index afb110e..43370a9 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -40,18 +40,24 @@ contract Halo2VerifyingKey { mstore({{ (32 * offset)|hex_padded(4) }}, {{ word|hex_padded(64) }}) // permutation_data [{{ loop.index0 }}] {%- endfor %} {%- let offset_7 = offset_6 + permutation_computations.len() %} - mstore({{ (32 * offset_7)|hex_padded(4) }}, {{ lookup_computations.end_ptr|hex_padded(64) }}) // end_ptr of lookup_computations + mstore({{ (32 * offset_7)|hex_padded(4) }}, {{ lookup_computations.meta_data|hex_padded(64) }}) // meta_data of lookup_computations {%- let base_offset = offset_7 + 1 %} {%- for lookup in lookup_computations.lookups %} - {%- let offset = base_offset + (loop.index0 * 3) + lookup.acc %} + {%- let offset = base_offset + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] + {%- let table_inputs = lookup.table_inputs %} + {%- let branching_offset %} + {%- if let Some(table_inputs) = table_inputs %} {%- for table_line in lookup.table_lines %} mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] {%- endfor %} - mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ lookup.table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] - mstore({{ (32 * (offset + 2 + lookup.table_lines.len()))|hex_padded(4) }}, {{ (32 * lookup.inputs.len())|hex_padded(64) }}) // outer_inputs_len[{{ loop.index0 }}] + mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] + {%- let branching_offset = offset + 1 + lookup.table_lines.len() %} + {%- else %} + {%- let branching_offset = offset %} + {%- endif %} {%- for input in lookup.inputs %} - {%- let offset = offset + loop.index0 + input.acc + 3 %} + {%- let offset = branching_offset + loop.index0 + input.acc %} {%- for expression in input.expression %} mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] {%- endfor %} From 3fac35c24745ae0c9e8925fcc4a62a7caede58e8 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 13 Aug 2024 20:16:57 -0400 Subject: [PATCH 50/65] refactor: verifier cache enum --- src/codegen.rs | 155 ++++++++++++++------------- src/codegen/template.rs | 83 +++++++++++--- src/codegen/util.rs | 30 ++---- templates/Halo2VerifyingArtifact.sol | 98 +++++++++++++++++ templates/Halo2VerifyingKey.sol | 76 +------------ 5 files changed, 264 insertions(+), 178 deletions(-) create mode 100644 templates/Halo2VerifyingArtifact.sol diff --git a/src/codegen.rs b/src/codegen.rs index 4b8afd9..8e329e9 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -9,7 +9,6 @@ use crate::codegen::{ expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, }, }; -use evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}; use halo2_proofs::{ halo2curves::{bn256, ff::Field}, plonk::VerifyingKey, @@ -22,7 +21,7 @@ use std::{ collections::HashMap, fmt::{self, Debug}, }; -use template::Halo2VerifierReusable; +use template::{Halo2VerifierReusable, Halo2VerifyingArtifact, VerifyingCache}; mod evaluator; mod pcs; @@ -159,7 +158,7 @@ impl<'a> SolidityGenerator<'a> { vk_writer: &mut impl fmt::Write, ) -> Result<(), fmt::Error> { self.generate_separate_verifier().render(verifier_writer)?; - self.generate_vk(true).render(vk_writer)?; + self.generate_verifying_artifact().render(vk_writer)?; Ok(()) } @@ -178,10 +177,6 @@ impl<'a> SolidityGenerator<'a> { ("vk_mptr", U256::from(0)), ("vk_len", U256::from(0)), ("num_instances", U256::from(0)), - ("num_advices_user_challenges_offset", U256::from(0)), - ("last_quotient_x_cptr", U256::from(0)), - ("first_quotient_x_cptr", U256::from(0)), - ("instance_cptr", U256::from(0)), ("k", U256::from(0)), ("n_inv", U256::from(0)), ("omega", U256::from(0)), @@ -201,6 +196,10 @@ impl<'a> SolidityGenerator<'a> { ("neg_s_g2_x_2", U256::from(0)), ("neg_s_g2_y_1", U256::from(0)), ("neg_s_g2_y_2", U256::from(0)), + ("num_advices_user_challenges_offset", U256::from(0)), + ("last_quotient_x_cptr", U256::from(0)), + ("first_quotient_x_cptr", U256::from(0)), + ("instance_cptr", U256::from(0)), ("challenges_offset", U256::from(0)), ("gate_computations_len_offset", U256::from(0)), ("permutation_computations_len_offset", U256::from(0)), @@ -236,9 +235,9 @@ impl<'a> SolidityGenerator<'a> { } } - fn generate_vk(&self, separate: bool) -> Halo2VerifyingKey { + fn generate_vk(&self, reusable: bool) -> Halo2VerifyingKey { // Get the dummy constants using the new function - let mut constants = Self::dummy_vk_constants(separate); + let mut constants = Self::dummy_vk_constants(reusable); // Fill in the actual values where applicable let domain = self.vk.get_domain(); @@ -315,21 +314,15 @@ impl<'a> SolidityGenerator<'a> { .tuples() .collect(); - let attached_vk = Halo2VerifyingKey { + Halo2VerifyingKey { constants: constants.clone(), fixed_comms: fixed_comms.clone(), permutation_comms: permutation_comms.clone(), - const_expressions: vec![], - num_advices_user_challenges: vec![], - gate_computations: GateDataEncoded::default(), - permutation_computations: PermutationDataEncoded::default(), - lookup_computations: LookupsDataEncoded::default(), - pcs_computations: pcs::PcsDataEncoded::default(), - }; - - if !separate { - return attached_vk; } + } + + fn generate_verifying_artifact(&self) -> Halo2VerifyingArtifact { + let mut dummy_vk = self.generate_vk(true); fn set_constant_value(constants: &mut [(&str, U256)], name: &str, value: U256) { if let Some((_, val)) = constants.iter_mut().find(|(n, _)| *n == name) { @@ -342,12 +335,14 @@ impl<'a> SolidityGenerator<'a> { .map(fr_to_u256) .collect::>(); - let vk_mptr_mock = - self.estimate_static_working_memory_size(&attached_vk, Ptr::calldata(0x84), false); + let vk_mptr_mock = self.estimate_static_working_memory_size( + &VerifyingCache::Key(&dummy_vk), + Ptr::calldata(0x84), + ); let dummy_data = Data::new( &self.meta, - &attached_vk, + &VerifyingCache::Key(&dummy_vk), Ptr::memory(vk_mptr_mock), Ptr::calldata(0x84), true, @@ -356,8 +351,8 @@ impl<'a> SolidityGenerator<'a> { let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); let offset = vk_mptr_mock - + (attached_vk.constants.len() * 0x20) - + (attached_vk.fixed_comms.len() + attached_vk.permutation_comms.len()) * 0x40; + + (dummy_vk.constants.len() * 0x20) + + (dummy_vk.fixed_comms.len() + dummy_vk.permutation_comms.len()) * 0x40; // keys to the map are the values of vk.const_expressions and values are the memory location of the vk.const_expressions. const_expressions.iter().enumerate().for_each(|(idx, _)| { @@ -406,6 +401,16 @@ impl<'a> SolidityGenerator<'a> { packed_words.push(U256::from(0)); bit_counter = 0; } + // Ensure that the packed num_advices and num_user_challenges data doesn't + // overflow. + assert!( + (*num_advices * 0x40) < 0x10000, + "num_advices * 0x40 must be less than 0x10000" + ); + assert!( + *num_user_challenges < 0x100, + "num_user_challenges must be less than 0x100" + ); packed_words[last_idx] |= U256::from(*num_advices * 0x40) << bit_counter; bit_counter += 16; packed_words[last_idx] |= U256::from(*num_user_challenges) << bit_counter; @@ -418,13 +423,11 @@ impl<'a> SolidityGenerator<'a> { }; // Update constants - let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); let instance_cptr = U256::from(self.meta.proof_len(self.scheme) + 0xa4); - let num_advices_user_challenges_offset = (constants.len() * 0x20) - + (fixed_comms.len() + permutation_comms.len()) * 0x40 - + (const_expressions.len() * 0x20); + let num_advices_user_challenges_offset = + dummy_vk.len(true) + (const_expressions.len() * 0x20); let gate_computations_len_offset = num_advices_user_challenges_offset + (num_advices_user_challenges.len() * 0x20); let permutations_computations_len_offset = @@ -434,48 +437,48 @@ impl<'a> SolidityGenerator<'a> { let pcs_computations_len_offset = lookup_computations_len_offset + (0x20 * lookup_computations_dummy.len()); - set_constant_value(&mut constants, "instance_cptr", instance_cptr); + set_constant_value(&mut dummy_vk.constants, "instance_cptr", instance_cptr); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "first_quotient_x_cptr", U256::from(first_quotient_x_cptr.value().as_usize()), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "last_quotient_x_cptr", U256::from(last_quotient_x_cptr.value().as_usize()), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "num_advices_user_challenges_offset", U256::from(num_advices_user_challenges_offset), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "gate_computations_len_offset", U256::from(gate_computations_len_offset), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "permutation_computations_len_offset", U256::from(permutations_computations_len_offset), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "lookup_computations_len_offset", U256::from(lookup_computations_len_offset), ); set_constant_value( - &mut constants, + &mut dummy_vk.constants, "pcs_computations_len_offset", U256::from(pcs_computations_len_offset), ); // Recreate the vk with the correct shape - let mut vk = Halo2VerifyingKey { - constants, - fixed_comms, - permutation_comms, + let mut vk = Halo2VerifyingArtifact { + constants: dummy_vk.constants, + fixed_comms: dummy_vk.fixed_comms, + permutation_comms: dummy_vk.permutation_comms, const_expressions, num_advices_user_challenges, gate_computations: gate_computations_dummy, @@ -485,18 +488,21 @@ impl<'a> SolidityGenerator<'a> { }; // Now generate the real vk_mptr with a vk that has the correct length - let vk_mptr = self.estimate_static_working_memory_size(&vk, Ptr::calldata(0x84), true); + let vk_mptr = self.estimate_static_working_memory_size( + &VerifyingCache::Artifact(&vk), + Ptr::calldata(0x84), + ); // replace the mock vk_mptr with the real vk_mptr set_constant_value(&mut vk.constants, "vk_mptr", U256::from(vk_mptr)); // replace the mock vk_len with the real vk_len - let vk_len = vk.len(); + let vk_len = vk.len(true); set_constant_value(&mut vk.constants, "vk_len", U256::from(vk_len)); // Generate the real data. let data = Data::new( &self.meta, - &vk, + &VerifyingCache::Artifact(&vk), Ptr::memory(vk_mptr), Ptr::calldata(0x84), true, @@ -537,9 +543,15 @@ impl<'a> SolidityGenerator<'a> { let proof_len_cptr = Ptr::calldata(0x6014F51944); let vk = self.generate_vk(false); - let vk_m = self.estimate_static_working_memory_size(&vk, proof_cptr, false); + let vk_m = self.estimate_static_working_memory_size(&VerifyingCache::Key(&vk), proof_cptr); let vk_mptr = Ptr::memory(vk_m); - let data = Data::new(&self.meta, &vk, vk_mptr, proof_cptr, false); + let data = Data::new( + &self.meta, + &VerifyingCache::Key(&vk), + vk_mptr, + proof_cptr, + false, + ); let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations: Vec> = chain![ @@ -599,12 +611,7 @@ impl<'a> SolidityGenerator<'a> { } } - fn estimate_static_working_memory_size( - &self, - vk: &Halo2VerifyingKey, - proof_cptr: Ptr, - separate: bool, - ) -> usize { + fn estimate_static_working_memory_size(&self, vk: &VerifyingCache, proof_cptr: Ptr) -> usize { let mock_vk_mptr = Ptr::memory(0x100000); let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr, false); let pcs_computation = match self.scheme { @@ -616,14 +623,14 @@ impl<'a> SolidityGenerator<'a> { Gwc19 => unimplemented!(), }; - let mut fsm_usage = itertools::max([ + let fsm_usage = itertools::max([ // Keccak256 input (can overwrite vk) itertools::max(chain![ self.meta.num_advices().into_iter().map(|n| n * 2 + 1), [self.meta.num_evals + 1], ]) .unwrap() - .saturating_sub(vk.len() / 0x20), + .saturating_sub(vk.len(true) / 0x20), // PCS computation pcs_computation, // Pairing @@ -631,24 +638,28 @@ impl<'a> SolidityGenerator<'a> { ]) .unwrap() * 0x20; - if separate { - let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); - let const_expressions = expression_consts(self.vk.cs()) - .into_iter() - .map(fr_to_u256) - .collect::>(); - const_expressions.iter().enumerate().for_each(|(idx, _)| { - let mptr = 0x20 * idx; - let mptr = Ptr::memory(mptr); - vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); - }); - let evaluator = - EvaluatorVK::new(self.vk.cs(), &self.meta, &mock, vk_lookup_const_table_dummy); - - let expression_eval_computations = evaluator.quotient_eval_fsm_usage(); - fsm_usage = itertools::max([fsm_usage, expression_eval_computations]).unwrap(); - }; - fsm_usage + // match statement for vk + match vk { + VerifyingCache::Artifact(_) => { + let mut vk_lookup_const_table_dummy: HashMap, Ptr> = + HashMap::new(); + let const_expressions = expression_consts(self.vk.cs()) + .into_iter() + .map(fr_to_u256) + .collect::>(); + const_expressions.iter().enumerate().for_each(|(idx, _)| { + let mptr = 0x20 * idx; + let mptr = Ptr::memory(mptr); + vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); + }); + let evaluator = + EvaluatorVK::new(self.vk.cs(), &self.meta, &mock, vk_lookup_const_table_dummy); + + let expression_eval_computations = evaluator.quotient_eval_fsm_usage(); + itertools::max([fsm_usage, expression_eval_computations]).unwrap() + } + _ => fsm_usage, + } } } diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 18be850..9ba1223 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -11,10 +11,34 @@ use super::{ evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}, pcs::PcsDataEncoded, }; +// Renderable trait for rendering logic +pub(crate) trait Renderable { + fn render(&self, writer: &mut dyn fmt::Write) -> Result<(), fmt::Error>; +} #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] pub(crate) struct Halo2VerifyingKey { + pub(crate) constants: Vec<(&'static str, U256)>, + pub(crate) fixed_comms: Vec<(U256, U256)>, + pub(crate) permutation_comms: Vec<(U256, U256)>, +} + +impl Halo2VerifyingKey { + pub(crate) fn len(&self, scaled: bool) -> usize { + let len = + self.constants.len() + (self.fixed_comms.len() + self.permutation_comms.len()) * 2; + if scaled { + len * 0x20 + } else { + len + } + } +} + +#[derive(Template)] +#[template(path = "Halo2VerifyingArtifact.sol")] +pub(crate) struct Halo2VerifyingArtifact { pub(crate) constants: Vec<(&'static str, U256)>, pub(crate) num_advices_user_challenges: Vec, pub(crate) fixed_comms: Vec<(U256, U256)>, @@ -26,19 +50,54 @@ pub(crate) struct Halo2VerifyingKey { pub(crate) pcs_computations: PcsDataEncoded, } -impl Halo2VerifyingKey { - pub(crate) fn len(&self) -> usize { - (self.constants.len() * 0x20) - + (self.fixed_comms.len() + self.permutation_comms.len()) * 0x40 - + (self.const_expressions.len() * 0x20) - + (self.num_advices_user_challenges.len() * 0x20) - + (self.gate_computations.len() * 0x20) - + (self.permutation_computations.len() * 0x20) - + (self.lookup_computations.len() * 0x20) - + (self.pcs_computations.len() * 0x20) +impl Halo2VerifyingArtifact { + pub(crate) fn len(&self, scaled: bool) -> usize { + let len = self.constants.len() + + (self.fixed_comms.len() + self.permutation_comms.len()) * 2 + + self.const_expressions.len() + + self.num_advices_user_challenges.len() + + self.gate_computations.len() + + self.permutation_computations.len() + + self.lookup_computations.len() + + self.pcs_computations.len(); + if scaled { + len * 0x20 + } else { + len + } } } +// Enum for handling both VerifyingKey and VerifyingArtifact +pub(crate) enum VerifyingCache<'a> { + Key(&'a Halo2VerifyingKey), + Artifact(&'a Halo2VerifyingArtifact), +} + +impl VerifyingCache<'_> { + pub(crate) fn len(&self, scaled: bool) -> usize { + match self { + VerifyingCache::Key(key) => key.len(scaled), + VerifyingCache::Artifact(artifact) => artifact.len(scaled), + } + } + + pub(crate) fn constants(&self) -> &Vec<(&'static str, U256)> { + match self { + VerifyingCache::Key(key) => &key.constants, + VerifyingCache::Artifact(artifact) => &artifact.constants, + } + } + + pub(crate) fn fixed_comms(&self) -> &Vec<(U256, U256)> { + match self { + VerifyingCache::Key(key) => &key.fixed_comms, + VerifyingCache::Artifact(artifact) => &artifact.fixed_comms, + } + } +} + +// Halo2Verifier struct and implementation #[derive(Template)] #[template(path = "Halo2Verifier.sol")] pub(crate) struct Halo2Verifier { @@ -67,8 +126,8 @@ pub(crate) struct Halo2VerifierReusable { pub(crate) vk_const_offsets: HashMap<&'static str, U256>, } -impl Halo2VerifyingKey { - pub(crate) fn render(&self, writer: &mut impl fmt::Write) -> Result<(), fmt::Error> { +impl Halo2VerifyingArtifact { + pub(crate) fn render(&self, writer: &mut (impl fmt::Write + ?Sized)) -> Result<(), fmt::Error> { self.render_into(writer).map_err(|err| match err { Error::Fmt(err) => err, _ => unreachable!(), diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 5177e70..d14587d 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -1,7 +1,4 @@ -use crate::codegen::{ - template::Halo2VerifyingKey, - BatchOpenScheme::{self, Bdfg21, Gwc19}, -}; +use crate::codegen::BatchOpenScheme::{self, Bdfg21, Gwc19}; use halo2_proofs::{ halo2curves::{bn256, ff::PrimeField, CurveAffine}, plonk::{Any, Column, ConstraintSystem, Expression, Gate}, @@ -15,6 +12,8 @@ use std::{ ops::{Add, Sub}, }; +use super::template::VerifyingCache; + #[derive(Debug)] #[cfg(feature = "mv-lookup")] pub(crate) struct ConstraintSystemMeta { @@ -391,21 +390,14 @@ pub(crate) struct Data { impl Data { pub(crate) fn new( meta: &ConstraintSystemMeta, - vk: &Halo2VerifyingKey, + vk: &VerifyingCache, vk_mptr: Ptr, proof_cptr: Ptr, separate: bool, ) -> Self { - let fixed_comm_mptr = vk_mptr + vk.constants.len(); - let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); - let challenge_mptr = permutation_comm_mptr - + (2 * vk.permutation_comms.len()) - + vk.const_expressions.len() - + vk.num_advices_user_challenges.len() - + (vk.gate_computations.len()) - + (vk.permutation_computations.len()) - + (vk.lookup_computations.len()) - + (vk.pcs_computations.len()); + let fixed_comm_mptr: Ptr = vk_mptr + vk.constants().len(); + let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms().len(); + let challenge_mptr = vk_mptr + vk.len(false); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; @@ -536,14 +528,14 @@ impl Data { impl Data { pub(crate) fn new( meta: &ConstraintSystemMeta, - vk: &Halo2VerifyingKey, + vk: &VerifyingCache, vk_mptr: Ptr, proof_cptr: Ptr, _separate: bool, ) -> Self { - let fixed_comm_mptr = vk_mptr + vk.constants.len(); - let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms.len(); - let challenge_mptr = permutation_comm_mptr + 2 * vk.permutation_comms.len(); + let fixed_comm_mptr: Ptr = vk_mptr + vk.constants().len(); + let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms().len(); + let challenge_mptr = vk_mptr + vk.len(false); let theta_mptr = challenge_mptr + meta.challenge_indices.len(); let advice_comm_start = proof_cptr; diff --git a/templates/Halo2VerifyingArtifact.sol b/templates/Halo2VerifyingArtifact.sol new file mode 100644 index 0000000..cdb40aa --- /dev/null +++ b/templates/Halo2VerifyingArtifact.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract Halo2VerifyingArtifact { + constructor() { + assembly { + {%- for (name, chunk) in constants %} + mstore({{ (32 * loop.index0)|hex_padded(4) }}, {{ chunk|hex_padded(64) }}) // {{ name }} + {%- endfor %} + {%- let offset_0 = constants.len() %} + {%- for (x, y) in fixed_comms %} + mstore({{ (32 * (offset_0 + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].x + mstore({{ (32 * (offset_0 + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // fixed_comms[{{ loop.index0 }}].y + {%- endfor %} + {%- let offset_1 = offset_0 + 2 * fixed_comms.len() %} + {%- for (x, y) in permutation_comms %} + mstore({{ (32 * (offset_1 + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x + mstore({{ (32 * (offset_1 + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y + {%- endfor %} + {%- let offset_2 = offset_1 + 2 * permutation_comms.len() %} + {%- for const in const_expressions %} + mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_3 = offset_2 + const_expressions.len() %} + {%- for word in num_advices_user_challenges %} + mstore({{ (32 * (offset_3 + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_4 = offset_3 + num_advices_user_challenges.len() %} + mstore({{ (32 * offset_4)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length + {%- let offset_5 = offset_4 + 1 %} + {%- for packed_expression_word in gate_computations.packed_expression_words %} + {%- let offset = offset_5 + loop.index0 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ packed_expression_word|hex_padded(64) }}) // packed_expression_word [{{ loop.index0 }}] + {%- endfor %} + {%- let offset_6 = offset_4 + gate_computations.len() %} + mstore({{ (32 * offset_6)|hex_padded(4) }}, {{ permutation_computations.permutation_meta_data|hex_padded(64) }}) // permutation_meta_data + {%- for word in permutation_computations.permutation_data %} + {%- let offset = offset_6 + 1 + loop.index0 %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ word|hex_padded(64) }}) // permutation_data [{{ loop.index0 }}] + {%- endfor %} + {%- let offset_7 = offset_6 + permutation_computations.len() %} + mstore({{ (32 * offset_7)|hex_padded(4) }}, {{ lookup_computations.meta_data|hex_padded(64) }}) // meta_data of lookup_computations + {%- let base_offset = offset_7 + 1 %} + {%- for lookup in lookup_computations.lookups %} + {%- let offset = base_offset + lookup.acc %} + mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] + {%- let table_inputs = lookup.table_inputs %} + {%- let branching_offset %} + {%- if let Some(table_inputs) = table_inputs %} + {%- for table_line in lookup.table_lines %} + mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] + {%- endfor %} + mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] + {%- let branching_offset = offset + 1 + lookup.table_lines.len() %} + {%- else %} + {%- let branching_offset = offset %} + {%- endif %} + {%- for input in lookup.inputs %} + {%- let offset = branching_offset + loop.index0 + input.acc %} + {%- for expression in input.expression %} + mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] + {%- endfor %} + mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] + {%- endfor %} + {%- endfor %} + {%- let offset_8 = offset_7 + lookup_computations.len() %} + {%- for point_word in pcs_computations.point_computations %} + mstore({{ (32 * (offset_8 + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_9 = offset_8 + pcs_computations.point_computations.len() %} + {%- for vanishing_word in pcs_computations.vanishing_computations %} + mstore({{ (32 * (offset_9 + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_10 = offset_9 + pcs_computations.vanishing_computations.len() %} + {%- for coeff_word in pcs_computations.coeff_computations %} + mstore({{ (32 * (offset_10 + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_11 = offset_10 + pcs_computations.coeff_computations.len() %} + mstore({{ (32 * offset_11)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations + {%- let offset_12 = offset_11 + 1 %} + {%- for r_eval_word in pcs_computations.r_evals_computations %} + mstore({{ (32 * (offset_12 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_13 = offset_12 + pcs_computations.r_evals_computations.len() %} + {%- for coeff_sum_word in pcs_computations.coeff_sums_computation %} + mstore({{ (32 * (offset_13 + loop.index0))|hex_padded(4) }}, {{ coeff_sum_word|hex_padded(64) }}) // coeff_sums_computations[{{ loop.index0 }}] + {%- endfor %} + {%- let offset_14 = offset_13 + pcs_computations.coeff_sums_computation.len() %} + mstore({{ (32 * offset_14)|hex_padded(4) }}, {{ pcs_computations.r_eval_computations|hex_padded(64) }}) // r_eval_computations + {%- let offset_15 = offset_14 + 1 %} + {%- for pairing_input_word in pcs_computations.pairing_input_computations %} + mstore({{ (32 * (offset_15 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] + {%- endfor %} + return(0, {{ (32 * (offset_8 + pcs_computations.len()))|hex() }}) + } + } +} diff --git a/templates/Halo2VerifyingKey.sol b/templates/Halo2VerifyingKey.sol index 43370a9..91b3b73 100644 --- a/templates/Halo2VerifyingKey.sol +++ b/templates/Halo2VerifyingKey.sol @@ -18,81 +18,7 @@ contract Halo2VerifyingKey { mstore({{ (32 * (offset_1 + 2 * loop.index0))|hex_padded(4) }}, {{ x|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].x mstore({{ (32 * (offset_1 + 2 * loop.index0 + 1))|hex_padded(4) }}, {{ y|hex_padded(64) }}) // permutation_comms[{{ loop.index0 }}].y {%- endfor %} - {%- let offset_2 = offset_1 + 2 * permutation_comms.len() %} - {%- for const in const_expressions %} - mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_3 = offset_2 + const_expressions.len() %} - {%- for word in num_advices_user_challenges %} - mstore({{ (32 * (offset_3 + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_4 = offset_3 + num_advices_user_challenges.len() %} - mstore({{ (32 * offset_4)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length - {%- let offset_5 = offset_4 + 1 %} - {%- for packed_expression_word in gate_computations.packed_expression_words %} - {%- let offset = offset_5 + loop.index0 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ packed_expression_word|hex_padded(64) }}) // packed_expression_word [{{ loop.index0 }}] - {%- endfor %} - {%- let offset_6 = offset_4 + gate_computations.len() %} - mstore({{ (32 * offset_6)|hex_padded(4) }}, {{ permutation_computations.permutation_meta_data|hex_padded(64) }}) // permutation_meta_data - {%- for word in permutation_computations.permutation_data %} - {%- let offset = offset_6 + 1 + loop.index0 %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ word|hex_padded(64) }}) // permutation_data [{{ loop.index0 }}] - {%- endfor %} - {%- let offset_7 = offset_6 + permutation_computations.len() %} - mstore({{ (32 * offset_7)|hex_padded(4) }}, {{ lookup_computations.meta_data|hex_padded(64) }}) // meta_data of lookup_computations - {%- let base_offset = offset_7 + 1 %} - {%- for lookup in lookup_computations.lookups %} - {%- let offset = base_offset + lookup.acc %} - mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] - {%- let table_inputs = lookup.table_inputs %} - {%- let branching_offset %} - {%- if let Some(table_inputs) = table_inputs %} - {%- for table_line in lookup.table_lines %} - mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] - {%- endfor %} - mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] - {%- let branching_offset = offset + 1 + lookup.table_lines.len() %} - {%- else %} - {%- let branching_offset = offset %} - {%- endif %} - {%- for input in lookup.inputs %} - {%- let offset = branching_offset + loop.index0 + input.acc %} - {%- for expression in input.expression %} - mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] - {%- endfor %} - mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] - {%- endfor %} - {%- endfor %} - {%- let offset_8 = offset_7 + lookup_computations.len() %} - {%- for point_word in pcs_computations.point_computations %} - mstore({{ (32 * (offset_8 + loop.index0))|hex_padded(4) }}, {{ point_word|hex_padded(64) }}) // point_computations[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_9 = offset_8 + pcs_computations.point_computations.len() %} - {%- for vanishing_word in pcs_computations.vanishing_computations %} - mstore({{ (32 * (offset_9 + loop.index0))|hex_padded(4) }}, {{ vanishing_word|hex_padded(64) }}) // vanishing_computations[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_10 = offset_9 + pcs_computations.vanishing_computations.len() %} - {%- for coeff_word in pcs_computations.coeff_computations %} - mstore({{ (32 * (offset_10 + loop.index0))|hex_padded(4) }}, {{ coeff_word|hex_padded(64) }}) // coeff_computations[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_11 = offset_10 + pcs_computations.coeff_computations.len() %} - mstore({{ (32 * offset_11)|hex_padded(4) }}, {{ pcs_computations.normalized_coeff_computations|hex_padded(64) }}) // normalized_coeff_computations - {%- let offset_12 = offset_11 + 1 %} - {%- for r_eval_word in pcs_computations.r_evals_computations %} - mstore({{ (32 * (offset_12 + loop.index0))|hex_padded(4) }}, {{ r_eval_word|hex_padded(64) }}) // r_evals_computations[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_13 = offset_12 + pcs_computations.r_evals_computations.len() %} - {%- for coeff_sum_word in pcs_computations.coeff_sums_computation %} - mstore({{ (32 * (offset_13 + loop.index0))|hex_padded(4) }}, {{ coeff_sum_word|hex_padded(64) }}) // coeff_sums_computations[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_14 = offset_13 + pcs_computations.coeff_sums_computation.len() %} - mstore({{ (32 * offset_14)|hex_padded(4) }}, {{ pcs_computations.r_eval_computations|hex_padded(64) }}) // r_eval_computations - {%- let offset_15 = offset_14 + 1 %} - {%- for pairing_input_word in pcs_computations.pairing_input_computations %} - mstore({{ (32 * (offset_15 + loop.index0))|hex_padded(4) }}, {{ pairing_input_word|hex_padded(64) }}) // pairing_input_computations[{{ loop.index0 }}] - {%- endfor %} - return(0, {{ (32 * (offset_8 + pcs_computations.len()))|hex() }}) + return(0, {{ (32 * (offset_1 + 2 * permutation_comms.len()))|hex() }}) } } } From 5a7ff4e02543f63e7efab5dcb60251d7ca50de6c Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 14 Aug 2024 16:31:11 -0400 Subject: [PATCH 51/65] *update readme *rmv separate param from functions --- .github/workflows/ci.yml | 2 + README.md | 14 ++- examples/separately.rs | 4 +- src/codegen.rs | 50 +++++----- src/codegen/evaluator.rs | 143 ++++++++-------------------- src/codegen/pcs.rs | 74 +++++--------- src/codegen/util.rs | 71 +------------- templates/Halo2VerifierReusable.sol | 7 +- 8 files changed, 107 insertions(+), 258 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a407832..0439ff0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,8 @@ jobs: - name: Run test run: cargo test --workspace --all-features --all-targets -- --nocapture + - name: Run separate example + run: cargo run --package halo2_solidity_verifier --example separately --all-features -- --nocapture lint: name: Lint diff --git a/README.md b/README.md index a53f557..d7c70e5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ For audited solidity verifier generator and proof aggregation toolkits, please r ## Usage -### Generate verifier and verifying key separately as 2 solidity contracts +### Generate fully reusable verifier and verifying artifact separately as 2 solidity contracts ```rust let generator = SolidityGenerator::new(¶ms, &vk, Bdfg21, num_instances); @@ -42,7 +42,7 @@ Note that function selector is already included. The [`Keccak256Transcript`](./src/transcript.rs#L19) behaves exactly same as the `EvmTranscript` in `snark-verifier`. -## Design Rationale +## Design Rationale for Conjoined Verifier The current solidity verifier generator within `snark-verifier` faces a couple of issues: @@ -51,6 +51,16 @@ The current solidity verifier generator within `snark-verifier` faces a couple o This repository is a ground-up rebuild, addressing these concerns while maintaining a focus on code size and readability. Remarkably, the gas cost is comparable, if not slightly lower, than the one generated by `snark-verifier`. +## Design Rationale for Reusable Verifier + +The previous `render_separately` solidity verifier, although granted some degree of reusability, was still dependent on a given circuit's configuation despite being independent of the verifying key. We wanted to reengineer the separate verifier to be completely independent of the circuit configuration, allowing for a single verifier to be used across multiple circuits. + +In the process we created two new types of contracts--`Halo2VerifierReusable` and `Halo2VerifierArtifact`-- that replaced the previous `Halo2Verifier` and `Halo2VerifierKey` contracts generated by the `render_seperately` compilation respectively. + +The `Halo2VerifierArtifact` extends the original `Halo2VerifierKey` by encoding all of the circuit configuration data that was hardcoded in the original separate `Halo2Verifier` into memory. The `Halo2VerifierReusable` then loads this configuration data dynamicaly from the `Halo2VerifierArtifact` at runtime, decodes it and executes the verification computation in a functionally identical manner to the conjoined version. + +For large circuits, this reduces deployment costs by 77 percent enabling the deployment of circuits that were previously infeasible due to the contract size limit, requiring an aggregation to get below the limit. + ## Acknowledgement The template is heavily inspired by Aztec's [`BaseUltraVerifier.sol`](https://github.com/AztecProtocol/barretenberg/blob/4c456a2b196282160fd69bead6a1cea85289af37/sol/src/ultra/BaseUltraVerifier.sol). diff --git a/examples/separately.rs b/examples/separately.rs index 52a54eb..5cc06b1 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -17,7 +17,7 @@ fn main() { let vk = keygen_vk(¶ms[&K_RANGE.start], &StandardPlonk::default()).unwrap(); let generator = SolidityGenerator::new(¶ms[&K_RANGE.start], &vk, Bdfg21, 0); let (verifier_solidity, _) = generator.render_separately().unwrap(); - save_solidity("Halo2Verifier.sol", &verifier_solidity); + save_solidity("Halo2VerifierReusable.sol", &verifier_solidity); let verifier_creation_code = compile_solidity(&verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); @@ -36,7 +36,7 @@ fn main() { let pk = keygen_pk(¶ms[&k], vk, &circuit).unwrap(); let generator = SolidityGenerator::new(¶ms[&k], pk.get_vk(), Bdfg21, num_instances); let (verifier_solidity, vk_solidity) = generator.render_separately().unwrap(); - save_solidity(format!("Halo2VerifyingKey-{k}.sol"), &vk_solidity); + save_solidity(format!("Halo2VerifyingArtifact-{k}.sol"), &vk_solidity); assert_eq!(deployed_verifier_solidity, verifier_solidity); diff --git a/src/codegen.rs b/src/codegen.rs index 8e329e9..3d32a78 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,10 +1,13 @@ use crate::codegen::{ - evaluator::{Evaluator, EvaluatorVK}, + evaluator::{EvaluatorDynamic, EvaluatorStatic}, pcs::{ - bdfg21_computations, queries, rotation_sets, + bdfg21_computations_dynamic, bdfg21_computations_static, queries, rotation_sets, BatchOpenScheme::{Bdfg21, Gwc19}, }, - template::{Halo2Verifier, Halo2VerifyingKey}, + template::{ + Halo2Verifier, Halo2VerifierReusable, Halo2VerifyingArtifact, Halo2VerifyingKey, + VerifyingCache, + }, util::{ expression_consts, fr_to_u256, g1_to_u256s, g2_to_u256s, ConstraintSystemMeta, Data, Ptr, }, @@ -15,13 +18,11 @@ use halo2_proofs::{ poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG, Rotation}, }; use itertools::{chain, Itertools}; -use pcs::bdfg21_computations_separate; use ruint::aliases::U256; use std::{ collections::HashMap, fmt::{self, Debug}, }; -use template::{Halo2VerifierReusable, Halo2VerifyingArtifact, VerifyingCache}; mod evaluator; mod pcs; @@ -151,7 +152,7 @@ impl<'a> SolidityGenerator<'a> { Ok(verifier_output) } - /// Render `Halo2Verifier.sol` and `Halo2VerifyingKey.sol` into writers. + /// Render `Halo2VerifierReusable.sol` and `Halo2VerifyingArtifact.sol` into writers. pub fn render_separately_into( &self, verifier_writer: &mut impl fmt::Write, @@ -162,7 +163,7 @@ impl<'a> SolidityGenerator<'a> { Ok(()) } - /// Render `Halo2Verifier.sol` and `Halo2VerifyingKey.sol` and return them as `String`. + /// Render `Halo2VerifierReusable.sol` and `Halo2VerifyingArtifact.sol` and return them as `String`. pub fn render_separately(&self) -> Result<(String, String), fmt::Error> { let mut verifier_output = String::new(); let mut vk_output = String::new(); @@ -345,7 +346,6 @@ impl<'a> SolidityGenerator<'a> { &VerifyingCache::Key(&dummy_vk), Ptr::memory(vk_mptr_mock), Ptr::calldata(0x84), - true, ); let mut vk_lookup_const_table_dummy: HashMap, Ptr> = HashMap::new(); @@ -361,7 +361,7 @@ impl<'a> SolidityGenerator<'a> { vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); }); - let evaluator_dummy = EvaluatorVK::new( + let evaluator_dummy = EvaluatorDynamic::new( self.vk.cs(), &self.meta, &dummy_data, @@ -374,7 +374,7 @@ impl<'a> SolidityGenerator<'a> { let lookup_computations_dummy = evaluator_dummy.lookup_computations(0); // Same for the pcs computations let pcs_computations_dummy = match self.scheme { - Bdfg21 => bdfg21_computations_separate(&self.meta, &dummy_data), + Bdfg21 => bdfg21_computations_dynamic(&self.meta, &dummy_data), Gwc19 => unimplemented!(), }; @@ -505,7 +505,6 @@ impl<'a> SolidityGenerator<'a> { &VerifyingCache::Artifact(&vk), Ptr::memory(vk_mptr), Ptr::calldata(0x84), - true, ); // Regenerate the gate computations with the correct offsets. @@ -527,7 +526,8 @@ impl<'a> SolidityGenerator<'a> { }); // Now we initalize the real evaluator_vk which will contain the correct offsets in the vk_lookup_const_table. - let evaluator = EvaluatorVK::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table); + let evaluator = + EvaluatorDynamic::new(self.vk.cs(), &self.meta, &data, vk_lookup_const_table); // NOTE: We don't need to replace the gate_computations_total_length since we are only potentially modifying the offsets for each constant mload operation. vk.gate_computations = evaluator.gate_computations(); @@ -545,19 +545,13 @@ impl<'a> SolidityGenerator<'a> { let vk = self.generate_vk(false); let vk_m = self.estimate_static_working_memory_size(&VerifyingCache::Key(&vk), proof_cptr); let vk_mptr = Ptr::memory(vk_m); - let data = Data::new( - &self.meta, - &VerifyingCache::Key(&vk), - vk_mptr, - proof_cptr, - false, - ); + let data = Data::new(&self.meta, &VerifyingCache::Key(&vk), vk_mptr, proof_cptr); - let evaluator = Evaluator::new(self.vk.cs(), &self.meta, &data); + let evaluator = EvaluatorStatic::new(self.vk.cs(), &self.meta, &data); let quotient_eval_numer_computations: Vec> = chain![ evaluator.gate_computations(), - evaluator.permutation_computations(false), - evaluator.lookup_computations(None, false), + evaluator.permutation_computations(), + evaluator.lookup_computations(), ] .enumerate() .map(|(idx, (mut lines, var))| { @@ -574,7 +568,7 @@ impl<'a> SolidityGenerator<'a> { .collect(); let pcs_computations = match self.scheme { - Bdfg21 => bdfg21_computations(&self.meta, &data, false), + Bdfg21 => bdfg21_computations_static(&self.meta, &data), Gwc19 => unimplemented!(), }; @@ -613,7 +607,7 @@ impl<'a> SolidityGenerator<'a> { fn estimate_static_working_memory_size(&self, vk: &VerifyingCache, proof_cptr: Ptr) -> usize { let mock_vk_mptr = Ptr::memory(0x100000); - let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr, false); + let mock = Data::new(&self.meta, vk, mock_vk_mptr, proof_cptr); let pcs_computation = match self.scheme { Bdfg21 => { let (superset, sets) = rotation_sets(&queries(&self.meta, &mock)); @@ -652,8 +646,12 @@ impl<'a> SolidityGenerator<'a> { let mptr = Ptr::memory(mptr); vk_lookup_const_table_dummy.insert(const_expressions[idx], mptr); }); - let evaluator = - EvaluatorVK::new(self.vk.cs(), &self.meta, &mock, vk_lookup_const_table_dummy); + let evaluator = EvaluatorDynamic::new( + self.vk.cs(), + &self.meta, + &mock, + vk_lookup_const_table_dummy, + ); let expression_eval_computations = evaluator.quotient_eval_fsm_usage(); itertools::max([fsm_usage, expression_eval_computations]).unwrap() diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 9d0640c..c2eebf8 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -9,15 +9,14 @@ use halo2_proofs::{ }, }; use itertools::{chain, izip, Itertools}; -use regex::Regex; use ruint::aliases::U256; use ruint::Uint; use std::{cell::RefCell, cmp::Ordering, collections::HashMap, iter}; -use super::util::{get_memory_ptr, Ptr, Word}; +use super::util::{Ptr, Word}; #[derive(Debug)] -pub(crate) struct Evaluator<'a, F: PrimeField> { +pub(crate) struct EvaluatorStatic<'a, F: PrimeField> { cs: &'a ConstraintSystem, meta: &'a ConstraintSystemMeta, data: &'a Data, @@ -25,7 +24,7 @@ pub(crate) struct Evaluator<'a, F: PrimeField> { var_cache: RefCell>, } -impl<'a, F> Evaluator<'a, F> +impl<'a, F> EvaluatorStatic<'a, F> where F: PrimeField, { @@ -52,20 +51,13 @@ where .collect() } - pub fn permutation_computations(&self, separate: bool) -> Vec<(Vec, String)> { + pub fn permutation_computations(&self) -> Vec<(Vec, String)> { let Self { meta, data, .. } = self; let last_chunk_idx = meta.num_permutation_zs - 1; - let theta_mptr = "theta_mptr"; - let beta = get_memory_ptr(theta_mptr, 1, &separate); - let gamma = get_memory_ptr(theta_mptr, 2, &separate); - let x_mptr = get_memory_ptr(theta_mptr, 4, &separate); - let l_last = get_memory_ptr(theta_mptr, 14, &separate); - let l_blind = get_memory_ptr(theta_mptr, 15, &separate); - let l_0 = get_memory_ptr(theta_mptr, 16, &separate); chain![ data.permutation_z_evals.first().map(|(z, _, _)| { vec![ - format!("let l_0 := mload({l_0})"), + format!("let l_0 := mload(L_0_MPTR)"), format!("let eval := addmod(l_0, sub(R, mulmod(l_0, {z}, R)), R)"), ] }), @@ -73,13 +65,13 @@ where let item = "addmod(mulmod(perm_z_last, perm_z_last, R), sub(R, perm_z_last), R)"; vec![ format!("let perm_z_last := {z}"), - format!("let eval := mulmod(mload({l_last}), {item}, R)"), + format!("let eval := mulmod(mload(L_LAST_MPTR), {item}, R)"), ] }), data.permutation_z_evals.iter().tuple_windows().map( |((_, _, z_i_last), (z_j, _, _))| { let item = format!("addmod({z_j}, sub(R, {z_i_last}), R)"); - vec![format!("let eval := mulmod(mload({l_0}), {item}, R)")] + vec![format!("let eval := mulmod(mload(L_0_MPTR), {item}, R)")] } ), izip!( @@ -91,8 +83,8 @@ where let last_column_idx = columns.len() - 1; chain![ [ - format!("let gamma := mload({})", gamma), - format!("let beta := mload({})", beta), + format!("let gamma := mload({})", "GAMMA_MPTR"), + format!("let beta := mload({})", "BETA_MPTR"), format!("let lhs := {}", evals.1), format!("let rhs := {}", evals.0), ], @@ -105,7 +97,7 @@ where )] }), (chunk_idx == 0) - .then(|| format!("mstore(0x00, mulmod(beta, mload({}), R))", x_mptr)), + .then(|| format!("mstore(0x00, mulmod(beta, mload({}), R))", "X_MPTR")), columns.iter().enumerate().flat_map(|(idx, column)| { let eval = self.eval(*column.column_type(), column.index(), 0); let item = format!("addmod(addmod({eval}, mload(0x00), R), gamma, R)"); @@ -116,7 +108,7 @@ where ] }), { - let item = format!("addmod(mload({l_last}), mload({l_blind}), R)"); + let item = format!("addmod(mload(L_LAST_MPTR), mload(L_BLIND_MPTR), R)"); let item = format!("sub(R, mulmod(left_sub_right, {item}, R))"); [ format!("let left_sub_right := addmod(lhs, sub(R, rhs), R)"), @@ -132,11 +124,7 @@ where } #[cfg(feature = "mv-lookup")] - pub fn lookup_computations( - &self, - vk_lookup_const_table: Option, super::util::Ptr>>, - separate: bool, - ) -> Vec<(Vec, String)> { + pub fn lookup_computations(&self) -> Vec<(Vec, String)> { let evaluate = |expressions: &Vec<_>| { // println!("expressions: {:?}", expressions); let (lines, inputs) = expressions @@ -162,16 +150,6 @@ where (inputs, table) }) .collect_vec(); - let lookup_const_table = if let Some(vk_lookup_const_table) = vk_lookup_const_table { - // map all the keys to u256_string - let vk_lookup_const_table: HashMap = vk_lookup_const_table - .iter() - .map(|(key, value)| (u256_string(*key), *value)) - .collect(); - Some(vk_lookup_const_table) - } else { - None - }; let vec = izip!(inputs_tables, &self.data.lookup_evals) .flat_map(|(inputs_tables, evals)| { @@ -179,31 +157,19 @@ where let num_inputs = inputs.len(); let (table_0, rest_tables) = tables.split_first().unwrap(); let (phi, phi_next, m) = evals; - // if separate then use the theta_mptr on set on the stack - // otherwise use the solidity constant - let theta_mptr = "theta_mptr"; - let theta = get_memory_ptr(theta_mptr, 0, &separate); - // For all the the other pointers offset from the theta_mptr perfrom relavant add operation - let beta = get_memory_ptr(theta_mptr, 1, &separate); - let l_last = get_memory_ptr(theta_mptr, 14, &separate); - - let l_blind = get_memory_ptr(theta_mptr, 15, &separate); - - let l_0 = get_memory_ptr(theta_mptr, 16, &separate); - // print line the input tables [ vec![ - format!("let l_0 := mload({l_0})"), + format!("let l_0 := mload(L_0_MPTR)"), format!("let eval := mulmod(l_0, {phi}, R)"), ], vec![ - format!("let l_last := mload({l_last})"), + format!("let l_last := mload(L_LAST_MPTR)"), format!("let eval := mulmod(l_last, {phi}, R)"), ], chain![ [ - format!("let theta := mload({})", theta).as_str(), - format!("let beta := mload({})", beta).as_str(), + format!("let theta := mload({})", "THETA_MPTR").as_str(), + format!("let beta := mload({})", "BETA_MPTR").as_str(), "let table" ] .map(str::to_string), @@ -218,35 +184,6 @@ where izip!(0.., inputs.into_iter()).flat_map(|(idx, (input_lines, inputs))| { let (input_0, rest_inputs) = inputs.split_first().unwrap(); let ident = format!("input_{idx}"); - let hex_regex = Regex::new(r":= (0x[0-9a-fA-F]+)").unwrap(); - // use regex to replace hex constants with mload format - let input_lines = - if let Some(lookup_const_table) = lookup_const_table.clone() { - // println!("lookup_const_table: {:?}", lookup_const_table); - let modified_input_lines: Vec = input_lines - .into_iter() - .map(|line| { - hex_regex - .replace_all(&line, |caps: ®ex::Captures| { - if let Some(hex_str) = caps.get(1) { - if let Some(ptr) = - lookup_const_table.get(hex_str.as_str()) - { - format!(":= mload({ptr})") - } else { - hex_str.as_str().to_string() - } - } else { - line.to_string() - } - }) - .to_string() - }) - .collect(); - modified_input_lines - } else { - input_lines - }; chain![ [format!("let {ident}")], code_block::<1, false>(chain![ @@ -294,7 +231,7 @@ where ]), { let l_inactive = - format!("addmod(mload({l_blind}), mload({l_last}), R)"); + format!("addmod(mload(L_BLIND_MPTR), mload(L_LAST_MPTR), R)"); let l_active = format!("addmod(1, sub(R, {l_inactive}), R)"); [format!( "let eval := mulmod({l_active}, addmod(lhs, sub(R, rhs), R), R)" @@ -310,11 +247,7 @@ where } #[cfg(not(feature = "mv-lookup"))] - pub fn lookup_computations( - &self, - _vk_lookup_const_table: Option, super::util::Ptr>>, - _separate: bool, - ) -> Vec<(Vec, String)> { + pub fn lookup_computations(&self) -> Vec<(Vec, String)> { let input_tables = self .cs .lookups() @@ -505,23 +438,12 @@ where } } -#[derive(Debug)] -pub(crate) struct EvaluatorVK<'a, F: PrimeField> { - cs: &'a ConstraintSystem, - #[allow(dead_code)] - meta: &'a ConstraintSystemMeta, - data: &'a Data, - static_mem_ptr: RefCell, - encoded_var_cache: RefCell>, - const_cache: RefCell, Ptr>>, -} - -// // Define an enum which catagorizes the operand memory location: -// // calldata_mptr -// // constant_mptr -// // instance_mptr -// // chllenge_mptr -// // static_memory_ptr +// Define an enum which catagorizes the operand memory location: +// calldata_mptr +// constant_mptr +// instance_mptr +// chllenge_mptr +// static_memory_ptr #[derive(Clone, PartialEq, Eq)] pub enum OperandMem { Calldata, @@ -653,7 +575,17 @@ impl LookupsDataEncoded { } } -impl<'a, F> EvaluatorVK<'a, F> +#[derive(Debug)] +pub(crate) struct EvaluatorDynamic<'a, F: PrimeField> { + cs: &'a ConstraintSystem, + meta: &'a ConstraintSystemMeta, + data: &'a Data, + static_mem_ptr: RefCell, + encoded_var_cache: RefCell>, + const_cache: RefCell, Ptr>>, +} + +impl<'a, F> EvaluatorDynamic<'a, F> where F: PrimeField, { @@ -692,7 +624,7 @@ where } #[allow(dead_code)] - pub fn gate_computation_fsm_usage(&self) -> usize { + fn gate_computation_fsm_usage(&self) -> usize { let packed_expression_words: Vec> = self .cs .gates() @@ -1106,7 +1038,8 @@ where }, &|_| { self.init_encoded_var( - U256::from(self.data.instance_eval.ptr().value().as_usize()), + // instance eval ptr located 17 words after the theta mptr + U256::from((self.data.theta_mptr + 17).value().as_usize()), OperandMem::Instance, ) }, diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index fc2dcbc..5ba4ca7 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -1,8 +1,6 @@ #![allow(clippy::useless_format)] -use crate::codegen::util::{ - for_loop, get_memory_ptr, ConstraintSystemMeta, Data, EcPoint, Location, Ptr, Word, -}; +use crate::codegen::util::{for_loop, ConstraintSystemMeta, Data, EcPoint, Location, Ptr, Word}; use itertools::{chain, izip, Itertools}; use ruint::aliases::U256; use std::collections::{BTreeMap, BTreeSet}; @@ -206,10 +204,9 @@ pub(crate) fn rotation_sets(queries: &[Query]) -> (BTreeSet, Vec Vec> { let queries = queries(meta, data); let (superset, sets) = rotation_sets(&queries); @@ -251,28 +248,11 @@ pub(crate) fn bdfg21_computations( let r_evals = Word::range(r_eval_mptr).take(sets.len()).collect_vec(); let sums = Word::range(sum_mptr).take(sets.len()).collect_vec(); - // if separate then we load in omega and omega_inv from vk_mptr + hardcoded offset. - // Otherwise we load in omega and omega_inv from the solidity constants. - - let vk_mptr = "vk_mptr"; - let theta_mptr = "theta_mptr"; - - let omega = get_memory_ptr(vk_mptr, 10, &separate); - - let omega_inv = get_memory_ptr(vk_mptr, 11, &separate); - - let g1_x = get_memory_ptr(vk_mptr, 17, &separate); - - let g1_y = get_memory_ptr(vk_mptr, 18, &separate); - - let x = get_memory_ptr(theta_mptr, 4, &separate); - - let zeta = get_memory_ptr(theta_mptr, 5, &separate); let point_computations = chain![ [ - format!("let x := mload({})", x).as_str(), - format!("let omega := mload({})", omega).as_str(), - format!("let omega_inv := mload({})", omega_inv).as_str(), + format!("let x := mload({})", "X_MPTR").as_str(), + format!("let omega := mload({})", "OMEGA_MPTR").as_str(), + format!("let omega_inv := mload({})", "OMEGA_INV_MPTR").as_str(), "let x_pow_of_omega := mulmod(x, omega, R)" ] .map(str::to_string), @@ -303,9 +283,8 @@ pub(crate) fn bdfg21_computations( .collect_vec(); // print the point computations // println!("{:?}", point_computations); - let mu = get_memory_ptr(theta_mptr, 7, &separate); let vanishing_computations = chain![ - [format!("let mu := mload({mu})").to_string()], + [format!("let mu := mload(MU_MPTR)").to_string()], { let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); let mptr_end = mptr + mu_minus_points.len(); @@ -418,7 +397,7 @@ pub(crate) fn bdfg21_computations( chain![ is_single_rot_set.then(|| format!("let coeff := {}", coeffs[0])), [ - format!("let zeta := mload({})", zeta).as_str(), + format!("let zeta := mload({})", "ZETA_MPTR").as_str(), "let r_eval := 0" ] .map(str::to_string), @@ -504,14 +483,6 @@ pub(crate) fn bdfg21_computations( ] .collect_vec() }); - - let nu = get_memory_ptr(theta_mptr, 6, &separate); - let mu = get_memory_ptr(theta_mptr, 7, &separate); - let r_eval = get_memory_ptr(theta_mptr, 21, &separate); - let pairing_lhs_x = get_memory_ptr(theta_mptr, 22, &separate); - let pairing_lhs_y = get_memory_ptr(theta_mptr, 23, &separate); - let pairing_rhs_x = get_memory_ptr(theta_mptr, 24, &separate); - let pairing_rhs_y = get_memory_ptr(theta_mptr, 25, &separate); let r_eval_computations = chain![ for_loop( [ @@ -544,16 +515,16 @@ pub(crate) fn bdfg21_computations( ] .map(str::to_string), [ - format!("r_eval := mulmod(r_eval, mload({nu}), R)").as_str(), + format!("r_eval := mulmod(r_eval, mload(NU_MPTR), R)").as_str(), "r_eval := addmod(r_eval, mulmod(mload(sum_inv_mptr), mload(r_eval_mptr), R), R)" ] .map(str::to_string), ), - [format!("mstore({r_eval}, r_eval)")], + [format!("mstore(R_EVAL_MPTR, r_eval)")], ] .collect_vec(); let pairing_input_computations = chain![ - [format!("let nu := mload({nu})").to_string()], + [format!("let nu := mload(NU_MPTR)").to_string()], izip!(0.., &sets, &diffs).flat_map(|(set_idx, set, set_coeff)| { let is_first_set = set_idx == 0; let is_last_set = set_idx == sets.len() - 1; @@ -582,7 +553,6 @@ pub(crate) fn bdfg21_computations( } }, ); - let zeta_mptr = ζ chain![ set.comms() .last() @@ -601,7 +571,7 @@ pub(crate) fn bdfg21_computations( .flat_map(|comm| { let (x, y) = (comm.x(), comm.y()); [ - format!("success := {ec_mul}(success, mload({zeta_mptr}))"), + format!("success := {ec_mul}(success, mload(ZETA_MPTR))"), format!("success := {ec_add}(success, {x}, {y})"), ] }) @@ -619,7 +589,7 @@ pub(crate) fn bdfg21_computations( "lt(mptr_end, mptr)", ["mptr := sub(mptr, 0x40)".to_string()], [ - format!("success := {ec_mul}(success, mload({zeta_mptr}))"), + format!("success := {ec_mul}(success, mload(ZETA_MPTR))"), format!("success := {ec_add}(success, {x}, {y})"), ], ) @@ -633,7 +603,7 @@ pub(crate) fn bdfg21_computations( format!("success := ec_mul_tmp(success, {scalar})"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), ], - (!is_last_set).then(|| format!("nu := mulmod(nu, mload({nu}), R)")) + (!is_last_set).then(|| format!("nu := mulmod(nu, mload(NU_MPTR), R)")) ] }) .into_iter() @@ -642,9 +612,9 @@ pub(crate) fn bdfg21_computations( .collect_vec() }), [ - format!("mstore(0x80, mload({}))", g1_x), - format!("mstore(0xa0, mload({}))", g1_y), - format!("success := ec_mul_tmp(success, sub(R, mload({r_eval})))"), + format!("mstore(0x80, mload({}))", "G1_X_MPTR"), + format!("mstore(0xa0, mload({}))", "G1_Y_MPTR"), + format!("success := ec_mul_tmp(success, sub(R, mload(R_EVAL_MPTR)))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w.x()), format!("mstore(0xa0, {})", w.y()), @@ -652,12 +622,12 @@ pub(crate) fn bdfg21_computations( format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), format!("mstore(0x80, {})", w_prime.x()), format!("mstore(0xa0, {})", w_prime.y()), - format!("success := ec_mul_tmp(success, mload({mu}))"), + format!("success := ec_mul_tmp(success, mload(MU_MPTR))"), format!("success := ec_add_acc(success, mload(0x80), mload(0xa0))"), - format!("mstore({pairing_lhs_x}, mload(0x00))"), - format!("mstore({pairing_lhs_y}, mload(0x20))"), - format!("mstore({pairing_rhs_x}, {})", w_prime.x()), - format!("mstore({pairing_rhs_y}, {})", w_prime.y()), + format!("mstore(PAIRING_LHS_X_MPTR, mload(0x00))"), + format!("mstore(PAIRING_LHS_Y_MPTR, mload(0x20))"), + format!("mstore(PAIRING_RHS_X_MPTR, {})", w_prime.x()), + format!("mstore(PAIRING_RHS_Y_MPTR, {})", w_prime.y()), ], ] .collect_vec(); @@ -701,7 +671,7 @@ impl PcsDataEncoded { } } -pub(crate) fn bdfg21_computations_separate( +pub(crate) fn bdfg21_computations_dynamic( meta: &ConstraintSystemMeta, data: &Data, ) -> PcsDataEncoded { diff --git a/src/codegen/util.rs b/src/codegen/util.rs index d14587d..95bb049 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -393,7 +393,6 @@ impl Data { vk: &VerifyingCache, vk_mptr: Ptr, proof_cptr: Ptr, - separate: bool, ) -> Self { let fixed_comm_mptr: Ptr = vk_mptr + vk.constants().len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms().len(); @@ -415,26 +414,10 @@ impl Data { let permutation_z_eval_cptr = permutation_eval_cptr + meta.num_permutations(); let lookup_eval_cptr = permutation_z_eval_cptr + 3 * meta.num_permutation_zs - 1; let w_cptr = lookup_eval_cptr + 3 * meta.num_lookups(); - let l_0 = if separate { - "add(theta_mptr, 0x220)" - } else { - "INSTANCE_EVAL_MPTR" - }; - let quotient_eval = if separate { - "add(theta_mptr, 0x240)" - } else { - "QUOTIENT_EVAL_MPTR" - }; - let quotient_x = if separate { - "add(theta_mptr, 0x260)" - } else { - "QUOTIENT_X_MPTR" - }; - let quotient_y = if separate { - "add(theta_mptr, 0x280)" - } else { - "QUOTIENT_Y_MPTR" - }; + let l_0 = "INSTANCE_EVAL_MPTR"; + let quotient_eval = "QUOTIENT_EVAL_MPTR"; + let quotient_x = "QUOTIENT_X_MPTR"; + let quotient_y = "QUOTIENT_Y_MPTR"; let fixed_comms = EcPoint::range(fixed_comm_mptr) .take(meta.num_fixeds) @@ -531,7 +514,6 @@ impl Data { vk: &VerifyingCache, vk_mptr: Ptr, proof_cptr: Ptr, - _separate: bool, ) -> Self { let fixed_comm_mptr: Ptr = vk_mptr + vk.constants().len(); let permutation_comm_mptr = fixed_comm_mptr + 2 * vk.fixed_comms().len(); @@ -1047,48 +1029,3 @@ where { U256::from(value).to_be_bytes() } - -pub(crate) fn get_memory_ptr(base_ptr: &str, offset: usize, separate: &bool) -> String { - if *separate { - if offset != 0 { - let hex_offset = format!("{:X}", offset * 32); - format!("add({}, 0x{})", base_ptr, hex_offset) - } else { - base_ptr.to_string() - } - } else { - match (offset, base_ptr) { - (10, "vk_mptr") => "OMEGA_MPTR".to_string(), - (11, "vk_mptr") => "OMEGA_INV_MPTR".to_string(), - (17, "vk_mptr") => "G1_X_MPTR".to_string(), - (18, "vk_mptr") => "G1_Y_MPTR".to_string(), - (0, "theta_mptr") => "THETA_MPTR".to_string(), - (1, "theta_mptr") => "BETA_MPTR".to_string(), - (2, "theta_mptr") => "GAMMA_MPTR".to_string(), - (3, "theta_mptr") => "Y_MPTR".to_string(), - (4, "theta_mptr") => "X_MPTR".to_string(), - (5, "theta_mptr") => "ZETA_MPTR".to_string(), - (6, "theta_mptr") => "NU_MPTR".to_string(), - (7, "theta_mptr") => "MU_MPTR".to_string(), - (8, "theta_mptr") => "ACC_LHS_X_MPTR".to_string(), - (9, "theta_mptr") => "ACC_LHS_Y_MPTR".to_string(), - (10, "theta_mptr") => "ACC_RHS_X_MPTR".to_string(), - (11, "theta_mptr") => "ACC_RHS_Y_MPTR".to_string(), - (12, "theta_mptr") => "X_N_MPTR".to_string(), - (13, "theta_mptr") => "X_N_MINUS_1_INV_MPTR".to_string(), - (14, "theta_mptr") => "L_LAST_MPTR".to_string(), - (15, "theta_mptr") => "L_BLIND_MPTR".to_string(), - (16, "theta_mptr") => "L_0_MPTR".to_string(), - (17, "theta_mptr") => "INSTANCE_EVAL_MPTR".to_string(), - (18, "theta_mptr") => "QUOTIENT_EVAL_MPTR".to_string(), - (19, "theta_mptr") => "QUOTIENT_X_MPTR".to_string(), - (20, "theta_mptr") => "QUOTIENT_Y_MPTR".to_string(), - (21, "theta_mptr") => "R_EVAL_MPTR".to_string(), - (22, "theta_mptr") => "PAIRING_LHS_X_MPTR".to_string(), - (23, "theta_mptr") => "PAIRING_LHS_Y_MPTR".to_string(), - (24, "theta_mptr") => "PAIRING_RHS_X_MPTR".to_string(), - (25, "theta_mptr") => "PAIRING_RHS_Y_MPTR".to_string(), - _ => panic!("Unknown offset: {}", offset), - } - } -} diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 3b5a0e2..b437c4a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -contract Halo2Verifier { +contract Halo2VerifierReusable { uint256 internal constant PROOF_LEN_CPTR = 0x64; uint256 internal constant PROOF_CPTR = 0x84; uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; @@ -851,8 +851,8 @@ contract Halo2Verifier { let last_idx := and(permutation_z_evals, 0xFF) permutation_z_evals := shr(8, permutation_z_evals) // Num of words scaled by 0x20 that take up each permutation eval (permutation_z_eval + column evals) - // first and second LSG bytes contain the number of words for all of the permutation evals except the last - // the third and fourth LSG bytes contain the number of words for the last permutation eval + // first and second LSG bytes contain the number of words for all of the permutation evals except the last. + // The third and fourth LSG bytes contain the number of words for the last permutation eval let num_words := and(permutation_z_evals, 0xFFFFFFFF) permutation_z_evals := shr(32, permutation_z_evals) permutation_z_evals_ptr := add(permutation_z_evals_ptr, 0x20) @@ -886,7 +886,6 @@ contract Halo2Verifier { quotient_eval_numer := z_evals( permutation_z_evals, - // Update the chunk offset to be in bytes num_words, perm_z_last_ptr, permutation_z_evals_ptr, From 99b65482befc78f06c6ac47aa25919cebe90091e Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 16 Aug 2024 10:42:32 -0400 Subject: [PATCH 52/65] *move encode mv lookup input table cache from metadata to lookup evals word. --- src/codegen/evaluator.rs | 60 ++++----- templates/Halo2VerifierReusable.sol | 190 ++++++++++++++-------------- 2 files changed, 128 insertions(+), 122 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index c2eebf8..ee45606 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -646,7 +646,7 @@ where let permutation_z_evals: Vec = data .permutation_z_evals .iter() - .map(|z| self.encode_triplet_evaluation_word(z)) + .map(|z| self.encode_triplet_evaluation_word(z, 0)) .collect(); let column_evals: Vec> = meta .permutation_columns @@ -663,7 +663,8 @@ where .collect() }) .collect(); - // num words each set of permutation data will take up (except the last one) scaled by 0x20 + // First lsg byte encodes the last index of the permutation_z_evals + // The next 2 bytes encode the num words each set of permutation data will take up (except the last one, we use the next 2 bytes for that) scaled by 0x20 // 48 is the bit offset of the permutation_z_evals and 40 is the bit offset of each column eval. let num_words = 1 + ((48 + (meta.permutation_chunk_len) * 40) / 256); let perm_meta_data: U256 = { @@ -766,7 +767,7 @@ where #[cfg(feature = "mv-lookup")] pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { let evaluate_table = |expressions: &Vec<_>| { - let offset = 0xa0; // offset to store theta offset ptrs used + let offset = 0xa0; // offset to store theta offset ptrs used in the reusable verifier (need to do this to avoid stack too deep errors) self.set_static_mem_ptr(offset); // println!("expressions: {:?}", expressions); let (lines, inputs) = expressions .iter() @@ -830,19 +831,10 @@ where let mut previous_table_lines: Option>> = None; - // Ensure that the number of inputs tables is less than 30 otherwise we won't be able to - // pack all of the shared input table expressions into the meta data. - assert!(inputs_tables.len() <= 30); - - // meta_data will encode the subsequence_indices as a bitmask, where each byte will store - // whether we use the previous table lines or not. If we use the previous table lines then - // the byte will be 0x0 otherwise it will be 0x1. - let mut meta_data = U256::from(0); let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) - .enumerate() // Add enumeration to track indices - .map(|(index, (inputs_tables, evals))| { + .map(|(inputs_tables, evals)| { let (inputs, (table_lines, table_inputs)) = inputs_tables.clone(); - let evals = self.encode_triplet_evaluation_word(evals); + let evals = self.encode_triplet_evaluation_word(evals, 8); let table_inputs = Some(self.encode_pack_ptrs(&table_inputs).unwrap()); let mut inner_accumulator = 0; let inputs: Vec = inputs @@ -866,22 +858,18 @@ where inputs: inputs.clone(), acc: accumulator, }; - - // bit offset to store the end_ptr - let offset = 16; - - // Handle subsequence indexing logic + // The first byte in the eval word will store whether we use the previous table lines or not. + // If we use the previous table lines then the byte will be 0x0 otherwise it will be 0x1. if let Some(prev_lines) = &previous_table_lines { if *prev_lines != table_lines { - meta_data |= U256::from(0x1) << ((index * 8) + offset); + lookup_encoded.evals |= U256::from(0x1); } else { lookup_encoded.table_lines = Vec::new(); lookup_encoded.table_inputs = None; } } else { - meta_data |= U256::from(0x1) << ((index * 8) + offset); + lookup_encoded.evals |= U256::from(0x1); } - accumulator += lookup_encoded.len(); previous_table_lines = Some(table_lines); @@ -889,14 +877,22 @@ where }) .collect_vec(); - let mut data = LookupsDataEncoded { lookups, meta_data }; - // Insert the end_ptr to the beginning of the meta data word. - data.meta_data |= U256::from((data.len() * 32) + offset); + if lookups.is_empty() { + return LookupsDataEncoded::default(); + } + + let mut data = LookupsDataEncoded { + lookups, + meta_data: U256::from(0), + }; + // Insert the num_words and end_ptr to the beginning of the meta data word. + let end_ptr = (data.len() * 32) + offset; + data.meta_data = U256::from(end_ptr); data } #[cfg(not(feature = "mv-lookup"))] - pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { + pub fn lookup_computations(&self, _offset: usize) -> LookupsDataEncoded { unimplemented!( "Lookup_computations function is not implemented for the non mv-lookup version of the verifier" ); @@ -949,11 +945,15 @@ where U256::from(op) | (ptr << 8) } - fn encode_triplet_evaluation_word(&self, value: &(Word, Word, Word)) -> U256 { + fn encode_triplet_evaluation_word( + &self, + value: &(Word, Word, Word), + bit_offset: usize, + ) -> U256 { let (z_i, z_j, z_i_last) = value; - U256::from(z_i.ptr().value().as_usize()) - | U256::from(z_j.ptr().value().as_usize() << 16) - | U256::from(z_i_last.ptr().value().as_usize() << 32) + U256::from(z_i.ptr().value().as_usize() << bit_offset) + | U256::from(z_j.ptr().value().as_usize() << (bit_offset + 16)) + | U256::from(z_i_last.ptr().value().as_usize() << (bit_offset + 32)) } // pack as many as 16 ptrs into a single word diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index b437c4a..0ca4a73 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -209,7 +209,7 @@ contract Halo2VerifierReusable { permutation_z_evals_ptr := next_z_ptr z := z_j } - // Due to the fact that permutation_columns.len() in H2 might not be divisble by permutation_chunk_len, the last column length might be less than permutation_chunk_len + // Due to the fact that permutation_columns.len() in H2 might not be divisible by permutation_chunk_len, the last column length might be less than permutation_chunk_len // We store this length in the last 16 bits of the num_words_packed word. num_words := and(shr(16, num_words_packed), 0xFFFF) col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) @@ -299,6 +299,100 @@ contract Halo2VerifierReusable { ret1 := addmod(ret1, beta, R) } + function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { + // iterate through the input_tables_len + let evals := mload(evals_ptr) + let new_table := and(evals, 0xFF) + evals := shr(8, evals) + let phi := and(evals, 0xFFFF) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x20),calldataload(phi), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x00), calldataload(phi), R), + R + ) + // load in the lookup_table_lines from the evals_ptr + evals_ptr := add(evals_ptr, 0x20) + // Due to the fact that lookups can share the previous table, we can cache it for reuse. + if new_table { + evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) + evals_ptr := add(evals_ptr, 0x20) + } + let input_expression := mload(evals_ptr) + // outer inputs len, stored in the first input expression word, shifted up by the free static memory offset of 0xa0 + let outer_inputs_len := and(input_expression, 0xFFFF) + input_expression := shr(16, input_expression) + for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { + // call the expression_evals function to evaluate the input_lines + let ident + evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression, mload(0x60), mload(0x80)) + evals_ptr := add(evals_ptr, 0x20) + input_expression := mload(evals_ptr) + // store ident in free static memory + mstore(j, ident) + } + let lhs + let rhs + switch eq(outer_inputs_len, 0x20) + case 1 { + rhs := table + } default { + // iterate through the outer_inputs_len + let last_idx := sub(outer_inputs_len, 0x20) + for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { + let tmp := mload(0xa0) + if eq(i, 0){ + tmp := mload(0xc0) + } + for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + if eq(i, j) { + continue + } + tmp := mulmod(tmp, mload(j), R) + + } + rhs := addmod(rhs, tmp, R) + if eq(i, last_idx) { + rhs := mulmod(rhs, table, R) + } + } + } + let tmp := mload(0xa0) + for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + tmp := mulmod(tmp, mload(j), R) + } + rhs := addmod( + rhs, + sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), + R + ) + lhs := mulmod( + mulmod(table, tmp, R), + addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + addmod( + 1, + sub(R, addmod(mload(0x40), mload(0x00), R)), + R + ), + addmod(lhs, sub(R, rhs), R), + R + ), + R + ) + ret0 := evals_ptr + ret1 := table + ret2 := quotient_eval_numer + } + function point_rots(pcs_computations, pcs_ptr, word_shift, x_pow_of_omega, omega) -> ret0, ret1 { // Extract the 32 LSG bits (4 bytes) from the pcs_computations word to get the max rot let values_max_rot := and(pcs_computations, 0xFFFFFFFF) @@ -903,103 +997,15 @@ contract Halo2VerifierReusable { mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind mstore(0x60, mload(theta_mptr)) // theta mstore(0x80, mload(add(theta_mptr, 0x20))) // beta - let evals_ptr, lookup_meta_data := soa_layout_metadata({{ + let evals_ptr, end_ptr := soa_layout_metadata({{ vk_const_offsets["lookup_computations_len_offset"]|hex() }}, vk_mptr) // lookup meta data contains 32 byte flags for indicating if we need to do a lookup table lines // expression evaluation or we can use the previous one cached in the table var. - if lookup_meta_data { + if end_ptr { let table - let end_ptr := and(lookup_meta_data, 0xFFFF) - lookup_meta_data := shr(16, lookup_meta_data) - // iterate through the input_tables_len for { } lt(evals_ptr, end_ptr) { } { - let evals := mload(evals_ptr) - let phi := and(evals, 0xFFFF) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod(mload(0x20),calldataload(phi), R), - R - ) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod(mload(0x00), calldataload(phi), R), - R - ) - // load in the lookup_table_lines from the evals_ptr - evals_ptr := add(evals_ptr, 0x20) - // Due to the fact that lookups share the previous table, we can cache the previous table. - if and(lookup_meta_data, 0xFF) { - evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - } - lookup_meta_data := shr(8, lookup_meta_data) - let input_expression := mload(evals_ptr) - // outer inputs len, stored in the first input expression word, shifted up by the free static memory offset of 0xa0 - let outer_inputs_len := and(input_expression, 0xFFFF) - input_expression := shr(16, input_expression) - for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { - // call the expression_evals function to evaluate the input_lines - let ident - evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression, mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - input_expression := mload(evals_ptr) - // store ident in free static memory - mstore(j, ident) - } - let lhs - let rhs - switch eq(outer_inputs_len, 0x20) - case 1 { - rhs := table - } default { - // iterate through the outer_inputs_len - let last_idx := sub(outer_inputs_len, 0x20) - for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { - let tmp := mload(0xa0) - if eq(i, 0){ - tmp := mload(0xc0) - } - for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { - if eq(i, j) { - continue - } - tmp := mulmod(tmp, mload(j), R) - - } - rhs := addmod(rhs, tmp, R) - if eq(i, last_idx) { - rhs := mulmod(rhs, table, R) - } - } - } - let tmp := mload(0xa0) - for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { - tmp := mulmod(tmp, mload(j), R) - } - rhs := addmod( - rhs, - sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), - R - ) - lhs := mulmod( - mulmod(table, tmp, R), - addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), - R - ) - quotient_eval_numer := addmod( - mulmod(quotient_eval_numer, y, R), - mulmod( - addmod( - 1, - sub(R, addmod(mload(0x40), mload(0x00), R)), - R - ), - addmod(lhs, sub(R, rhs), R), - R - ), - R - ) + evals_ptr, table, quotient_eval_numer := mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) } } } From 3082fda94151fc6760a3cb2be4741ddbeef04c03 Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:03:17 -0400 Subject: [PATCH 53/65] chore: update h2 curves --- Cargo.toml | 2 +- src/codegen/evaluator.rs | 4 ++-- src/codegen/util.rs | 4 ++-- src/test.rs | 26 +++++++++++++------------- src/transcript.rs | 24 ++++++++++++------------ 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d689458..0ae2f1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", branch = "main" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", rev = "38abade26078496aa4d8738b967b0e7fa5d9505c" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 39428b9..26f894b 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -23,7 +23,7 @@ pub(crate) struct Evaluator<'a, F: PrimeField> { impl<'a, F> Evaluator<'a, F> where - F: PrimeField, + F: PrimeField>, { pub(crate) fn new( cs: &'a ConstraintSystem, @@ -470,7 +470,7 @@ fn evaluate( scaled: &impl Fn(T, U256) -> T, ) -> T where - F: PrimeField, + F: PrimeField>, { let evaluate = |expr| { evaluate( diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 1f31ea7..83c2267 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -911,9 +911,9 @@ pub(crate) fn fr_to_u256(fe: impl Borrow) -> U256 { pub(crate) fn fe_to_u256(fe: impl Borrow) -> U256 where - F: PrimeField, + F: PrimeField>, { - U256::from_le_bytes(fe.borrow().to_repr()) + U256::from_le_bytes(fe.borrow().to_repr().inner().clone()) } pub(crate) fn to_u256_be_bytes(value: T) -> [u8; 32] diff --git a/src/test.rs b/src/test.rs index 75b631c..8308753 100644 --- a/src/test.rs +++ b/src/test.rs @@ -216,8 +216,8 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::Base: PrimeField, - M::Fr: PrimeField, + ::Base: PrimeField>, + M::Fr: PrimeField>, { let s = M::Fr::random(&mut rng); let g1 = M::G1Affine::generator(); @@ -242,8 +242,8 @@ mod halo2 { fn ec_point_to_limbs(ec_point: impl Borrow, num_limb_bits: usize) -> Vec where C: CurveAffine, - C::Base: PrimeField, - C::Scalar: PrimeField, + C::Base: PrimeField>, + C::Scalar: PrimeField>, { let coords = ec_point.borrow().coordinates().unwrap(); [*coords.x(), *coords.y()] @@ -254,8 +254,8 @@ mod halo2 { fn fe_to_limbs(fe: impl Borrow, num_limb_bits: usize) -> Vec where - F1: PrimeField, - F2: PrimeField, + F1: PrimeField>, + F2: PrimeField>, { let big = U256::from_le_bytes(fe.borrow().to_repr()); let mask = &((U256::from(1) << num_limb_bits) - U256::from(1)); @@ -268,7 +268,7 @@ mod halo2 { fn fe_from_u256(u256: impl Borrow) -> F where - F: PrimeField, + F: PrimeField>, { let bytes = u256.borrow().to_le_bytes::<32>(); F::from_repr_vartime(bytes).unwrap() @@ -303,9 +303,9 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField, - ::Base: PrimeField, - M::Fr: PrimeField, + ::ScalarExt: PrimeField>, + ::Base: PrimeField>, + M::Fr: PrimeField>, { fn min_k() -> u32 { 6 @@ -538,9 +538,9 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField, - ::Base: PrimeField, - M::Fr: PrimeField, + ::ScalarExt: PrimeField>, + ::Base: PrimeField>, + M::Fr: PrimeField>, { fn min_k() -> u32 { 9 diff --git a/src/transcript.rs b/src/transcript.rs index f125329..d38df1c 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -37,12 +37,12 @@ impl Keccak256Transcript { 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; 0x20]; @@ -58,7 +58,7 @@ where impl Transcript> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn squeeze_challenge(&mut self) -> ChallengeEvm { let buf_len = self.buf.len(); @@ -95,7 +95,7 @@ where impl TranscriptRead> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn read_point(&mut self) -> io::Result { let mut reprs = [::Repr::default(); 2]; @@ -121,7 +121,7 @@ where let mut data = [0; 0x20]; self.stream.read_exact(data.as_mut())?; data.reverse(); - let scalar = C::Scalar::from_repr_vartime(data) + let scalar = C::Scalar::from_repr_vartime(data.into()) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid scalar".to_string()))?; Transcript::>::common_scalar(self, scalar)?; Ok(scalar) @@ -131,7 +131,7 @@ where impl TranscriptReadBuffer> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(reader: R) -> Self { Keccak256Transcript::new(reader) @@ -141,7 +141,7 @@ where impl TranscriptWrite> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn write_point(&mut self, ec_point: C) -> io::Result<()> { self.common_point(ec_point)?; @@ -165,7 +165,7 @@ where impl TranscriptWriterBuffer> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(writer: W) -> Self { Keccak256Transcript::new(writer) @@ -178,15 +178,15 @@ where fn u256_to_fe(value: U256) -> F where - F: PrimeField, + F: PrimeField>, { let value = value % modulus::(); - F::from_repr(value.to_le_bytes::<0x20>()).unwrap() + F::from_repr(value.to_le_bytes::<0x20>().into()).unwrap() } 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().inner().clone()) + U256::from(1) } From fdfe5e45c71e3dfa62b20645362e4a888a920aba Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 21 Aug 2024 18:35:38 -0300 Subject: [PATCH 54/65] *add compiler warning for non contigious table expressions across lookups. --- src/codegen/evaluator.rs | 56 +++++++++++++++++++++++------ templates/Halo2VerifierReusable.sol | 6 ++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index ee45606..3bddac9 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -453,7 +453,7 @@ pub enum OperandMem { StaticMemory, } -// Holds the encoded data stored in the separate VK +// Holds the encoded data stored in the VK artifact // needed to perform the gate computations of // the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq, Default)] @@ -472,7 +472,7 @@ impl GateDataEncoded { } } -// Holds the encoded data stored in the separate VK +// Holds the encoded data stored in the VK artifact // needed to perform the permutation computations of // the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq)] @@ -507,9 +507,8 @@ pub struct InputsEncoded { pub(crate) acc: usize, } -// Holds the encoded data stored in the separate VK +// Holds the encoded data stored in the VK artifact // needed to perform the lookup computations for one lookup table -// in the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq)] pub struct LookupEncoded { pub(crate) evals: U256, @@ -520,13 +519,17 @@ pub struct LookupEncoded { } // For each element of the lookups vector we have a word for: -// 1) the evals (cptr, cptr, cptr), +// 1) the evals (new_table: bool, (cptr, cptr, cptr)) // 2) table_lines Vec // 3) table_inputs Vec packed into a single (throws an error if table_inputs.len() > 16) +// these values represent the set of table_lines results that are used to compute the accumulator evaluation of the +// lookup table. // 4) outer_inputs_len (inputs.0.len()) // For each element of the inputs vector in LookupEncoded we have a word for: // 1) inputs (inputs[i].expressions) // 2) input_vars Vec packed into a single (throws an error if > 16) +// these values represent the set of input expression results that are used to compute the accumulator evaluation of the +// lookup inputs. // Then we have a word for each step in the expression evaluation. This is the // sum of the lengths of the inputs. impl LookupEncoded { @@ -547,7 +550,7 @@ impl LookupEncoded { } } -// Holds the encoded data stored in the separate VK +// Holds the encoded data stored in the VK artifact // needed to perform the lookup computations of all the lookup tables // needed in the quotient evaluation portion of the reusable verifier. #[derive(Clone, PartialEq, Eq)] @@ -777,7 +780,7 @@ where acc.1.push(result.1); acc }); - assert!(inputs.len() <= 16); + assert!(inputs.len() <= 16, "lookup_table_inputs.len() > 16"); self.reset(); let lines_packed = self.encode_pack_expr_operations(lines, 8); (lines_packed, inputs) @@ -798,12 +801,25 @@ where acc }); self.reset(); + assert!(inputs.len() <= 16, "input_vars.len() > 16"); // bit offset to store the number of inputs let bit_offset = if idx == 0 { 24 } else { 8 }; let lines_packed = self.encode_pack_expr_operations(lines, bit_offset); (lines_packed, inputs) }; + let is_contiguous = |positions: &[usize]| -> bool { + if positions.is_empty() { + return true; + } + for window in positions.windows(2) { + if window[1] != window[0] + 1 { + return false; + } + } + true + }; + let inputs_tables = self .cs .lookups() @@ -830,9 +846,12 @@ where let mut accumulator = 0; let mut previous_table_lines: Option>> = None; + // Hshmap used to check for contigious table_expressions in the lookups + let mut table_expression_positions: HashMap>, Vec> = HashMap::new(); let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) - .map(|(inputs_tables, evals)| { + .enumerate() + .map(|(lookup_index, (inputs_tables, evals))| { let (inputs, (table_lines, table_inputs)) = inputs_tables.clone(); let evals = self.encode_triplet_evaluation_word(evals, 8); let table_inputs = Some(self.encode_pack_ptrs(&table_inputs).unwrap()); @@ -851,6 +870,11 @@ where }) .collect_vec(); + table_expression_positions + .entry(table_lines.clone()) + .or_default() + .push(lookup_index); + let mut lookup_encoded = LookupEncoded { evals, table_lines: table_lines.clone(), @@ -877,6 +901,18 @@ where }) .collect_vec(); + // Check if any table_expressions (Vec>) have non-contiguous positions + for (table_exprs, positions) in table_expression_positions.iter() { + if !is_contiguous(positions) { + eprintln!( + "Warning: The table expressions `{:?}` are not contiguous across lookups. \ + Consider reordering your lookups so that all occurrences of this table expression \ + are adjacent to each other. This will result in more gas efficient verifications", + table_exprs + ); + } + } + if lookups.is_empty() { return LookupsDataEncoded::default(); } @@ -978,9 +1014,9 @@ where for expr in exprs.iter() { let first_byte = expr.as_limbs()[0] & 0xFF; let offset = if first_byte == 0 || first_byte == 1 { - 24 + 24 // single operand operation bit offset } else { - 40 + 40 // multi operand operation bit offset }; let mut next_bit_counter = bit_counter + offset; diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 0ca4a73..4f7d459 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -283,7 +283,7 @@ contract Halo2VerifierReusable { function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word, theta, beta) -> ret0, ret1 { let idx - let vars // packed vars with mload cptrs to load in the vars stored in static memory in the + let vars // packed vars with ptrs to load in the vars stored in static memory in the // expression evaluation. ret0, vars, idx := expression_evals_packed(fsmp, code_ptr, expressions_word) ret1 := mload(and(vars, 0xFFFF)) // initlaize the accumulator with the first value in the vars @@ -302,6 +302,7 @@ contract Halo2VerifierReusable { function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { // iterate through the input_tables_len let evals := mload(evals_ptr) + // We store a boolean flag in the first LSG byte of the evals ptr to determine if we need to load in a new table or reuse the previous table. let new_table := and(evals, 0xFF) evals := shr(8, evals) let phi := and(evals, 0xFFFF) @@ -323,9 +324,10 @@ contract Halo2VerifierReusable { evals_ptr := add(evals_ptr, 0x20) } let input_expression := mload(evals_ptr) - // outer inputs len, stored in the first input expression word, shifted up by the free static memory offset of 0xa0 + // outer inputs len, stored in the first input expression word let outer_inputs_len := and(input_expression, 0xFFFF) input_expression := shr(16, input_expression) + // shift up the inputs iterator by the free static memory offset of 0xa0 for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { // call the expression_evals function to evaluate the input_lines let ident From bf04cd4e0b3e364a9989d0e0d54e5da59cb1ed6c Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 25 Aug 2024 16:04:35 -0300 Subject: [PATCH 55/65] *bitmask constants *pack input vars of lookup expressions --- src/codegen/evaluator.rs | 95 ++++---- src/codegen/pcs.rs | 8 +- templates/Halo2VerifierReusable.sol | 338 ++++++++++++++------------- templates/Halo2VerifyingArtifact.sol | 13 +- 4 files changed, 232 insertions(+), 222 deletions(-) diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 3bddac9..41068a6 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -503,7 +503,6 @@ impl PermutationDataEncoded { #[derive(Clone, PartialEq, Eq)] pub struct InputsEncoded { pub(crate) expression: Vec, - pub(crate) vars: U256, pub(crate) acc: usize, } @@ -513,7 +512,6 @@ pub struct InputsEncoded { pub struct LookupEncoded { pub(crate) evals: U256, pub(crate) table_lines: Vec, - pub(crate) table_inputs: Option, pub(crate) acc: usize, pub(crate) inputs: Vec, } @@ -534,13 +532,8 @@ pub struct LookupEncoded { // sum of the lengths of the inputs. impl LookupEncoded { pub fn len(&self) -> usize { - let base_len = if self.table_inputs.is_none() { - 1 // Add 2 if table_inputs is none - } else { - 2 // Add 3 otherwise - }; + let base_len = 1; // Add 2 if table_inputs is none base_len - + (self.inputs.len()) + self .inputs .iter() @@ -780,10 +773,8 @@ where acc.1.push(result.1); acc }); - assert!(inputs.len() <= 16, "lookup_table_inputs.len() > 16"); self.reset(); - let lines_packed = self.encode_pack_expr_operations(lines, 8); - (lines_packed, inputs) + self.encode_pack_expr_operations(lines, 8, Some(inputs)) }; let evaluate_inputs = |idx: usize, expressions: &Vec<_>| { @@ -801,11 +792,9 @@ where acc }); self.reset(); - assert!(inputs.len() <= 16, "input_vars.len() > 16"); // bit offset to store the number of inputs let bit_offset = if idx == 0 { 24 } else { 8 }; - let lines_packed = self.encode_pack_expr_operations(lines, bit_offset); - (lines_packed, inputs) + self.encode_pack_expr_operations(lines, bit_offset, Some(inputs)) }; let is_contiguous = |positions: &[usize]| -> bool { @@ -831,11 +820,11 @@ where let inputs = inputs_iter .clone() .map(|(idx, expressions)| { - let (mut lines, inputs) = evaluate_inputs(idx, expressions); + let mut lines = evaluate_inputs(idx, expressions); if idx == 0 { lines[0] |= U256::from(outer_inputs_len); } - (lines, inputs) + lines }) .collect_vec(); let table = evaluate_table(lookup.table_expressions()); @@ -852,20 +841,17 @@ where let lookups: Vec = izip!(inputs_tables, &self.data.lookup_evals) .enumerate() .map(|(lookup_index, (inputs_tables, evals))| { - let (inputs, (table_lines, table_inputs)) = inputs_tables.clone(); + let (inputs, table_lines) = inputs_tables.clone(); let evals = self.encode_triplet_evaluation_word(evals, 8); - let table_inputs = Some(self.encode_pack_ptrs(&table_inputs).unwrap()); let mut inner_accumulator = 0; let inputs: Vec = inputs .iter() - .map(|(input_lines, inputs)| { - let inputs = self.encode_pack_ptrs(inputs).unwrap(); + .map(|input_lines| { let res = InputsEncoded { expression: input_lines.clone(), - vars: inputs, acc: inner_accumulator, }; - inner_accumulator += input_lines.len() + 1; + inner_accumulator += input_lines.len(); res }) .collect_vec(); @@ -878,7 +864,6 @@ where let mut lookup_encoded = LookupEncoded { evals, table_lines: table_lines.clone(), - table_inputs, inputs: inputs.clone(), acc: accumulator, }; @@ -889,7 +874,6 @@ where lookup_encoded.evals |= U256::from(0x1); } else { lookup_encoded.table_lines = Vec::new(); - lookup_encoded.table_inputs = None; } } else { lookup_encoded.evals |= U256::from(0x1); @@ -992,21 +976,12 @@ where | U256::from(z_i_last.ptr().value().as_usize() << (bit_offset + 32)) } - // pack as many as 16 ptrs into a single word - // throws an error if the number of ptrs is greater than 16 - fn encode_pack_ptrs(&self, ptrs: &[U256]) -> Result { - if ptrs.len() > 16 { - return Err("Number of pointers cannot be greater than 16"); - } - - let mut packed = U256::from(0); - for (i, ptr) in ptrs.iter().enumerate() { - packed |= *ptr << (i * 16); - } - Ok(packed) - } - - fn encode_pack_expr_operations(&self, exprs: Vec, mut bit_counter: i32) -> Vec { + fn encode_pack_expr_operations( + &self, + exprs: Vec, + mut bit_counter: i32, + lookup_inputs: Option>, + ) -> Vec { let mut packed_words: Vec = vec![U256::from(0)]; let mut last_idx = 0; let initial_offset = bit_counter; @@ -1019,16 +994,44 @@ where 40 // multi operand operation bit offset }; - let mut next_bit_counter = bit_counter + offset; + let next_bit_counter = bit_counter + offset; if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); - next_bit_counter = offset; - packed_words[last_idx] = *expr - } else { - packed_words[last_idx] |= *expr << bit_counter; + bit_counter = 0; + } + packed_words[last_idx] |= *expr << bit_counter; + bit_counter += offset; + } + if let Some(inputs) = lookup_inputs { + // 1 byte for op flag (0x04) followed by 1 byte for the number of words allocated. + let offset = 16; + let next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + let start_idx = last_idx; + packed_words[last_idx] |= U256::from(4_u8) << bit_counter; + bit_counter += 8; + let length_bit_offset = bit_counter; + bit_counter += 8; + // iterate over the inputs + for input in inputs.iter() { + let offset = 16; + let next_bit_counter = bit_counter + offset; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + packed_words[last_idx] |= *input << bit_counter; + bit_counter += 16; } - bit_counter = next_bit_counter; + let allocated_words = last_idx - start_idx + 1; + // Encode the number of words allocated for the lookup input vars (after the 0x04 flag) + packed_words[start_idx] |= U256::from(allocated_words) << length_bit_offset; } let packed_words_len = packed_words.len(); @@ -1046,7 +1049,7 @@ where self.reset(); let res = result.0; if pack { - self.encode_pack_expr_operations(res, 8) + self.encode_pack_expr_operations(res, 8, None) } else { res } diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 5ba4ca7..06ce59e 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -716,12 +716,14 @@ pub(crate) fn bdfg21_computations_dynamic( let point_computations: Vec = { let pack_words = |points: Vec, interm_point: Option| { let mut packed_words: Vec = vec![U256::from(0)]; - let mut bit_counter = 32; + let mut bit_counter = 8; let points_len = points.len(); + // assert that points_len is less than 256 bits so that it fits into 1 byte. + assert!(points_len < 256); if let Some(interm_point) = interm_point { packed_words[0] |= interm_point; packed_words[0] |= U256::from(points_len) << 16; - bit_counter = 48; + bit_counter = 24; } else { packed_words[0] |= U256::from(points_len); } @@ -814,7 +816,7 @@ pub(crate) fn bdfg21_computations_dynamic( packed_word |= U256::from(0x20) << offset; offset += 16; } - // pack a blank word + // pack a blank ptr packed_word |= U256::from(0) << offset; offset += 16; // pack the diff.ptr() diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 4f7d459..ffa2d00 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -8,6 +8,8 @@ contract Halo2VerifierReusable { uint256 internal constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 internal constant R = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // BN254 scalar field uint256 internal constant DELTA = 4131629893567559867359510883348571134090853742863529169391034518566172092834; + uint256 internal constant PTR_BITMASK = 0xFFFF; + uint256 internal constant BYTE_FLAG_BITMASK = 0xFF; @@ -169,17 +171,17 @@ contract Halo2VerifierReusable { let l_blind := mload(add(theta_mptr, 0x1e0)) let i_eval := mload(add(theta_mptr, 0x220)) // Extract the index 1 and index 0 z evaluations from the z word. - let lhs := calldataload(and(shr(16,z), 0xFFFF)) - let rhs := calldataload(and(z, 0xFFFF)) + let lhs := calldataload(and(shr(16,z), PTR_BITMASK)) + let rhs := calldataload(and(z, PTR_BITMASK)) z := shr(48, z) // loop through the word_len_chunk for { let j := 0 } lt(j, num_words) { j := add(j, 0x20) } { for { } z { } { let eval := i_eval - if eq(and(z, 0xFF), 0x00) { - eval := calldataload(and(shr(8, z), 0xFFFF)) + if eq(and(z, BYTE_FLAG_BITMASK), 0x00) { + eval := calldataload(and(shr(8, z), PTR_BITMASK)) } - lhs := mulmod(lhs, addmod(addmod(eval, mulmod(beta, calldataload(and(shr(24, z), 0xFFFF)), R), R), gamma, R), R) + lhs := mulmod(lhs, addmod(addmod(eval, mulmod(beta, calldataload(and(shr(24, z), PTR_BITMASK)), R), R), gamma, R), R) rhs := mulmod(rhs, addmod(addmod(eval, mload(0x00), R), gamma, R), R) z := shr(40, z) mstore(0x00, mulmod(mload(0x00), DELTA, R)) @@ -193,7 +195,7 @@ contract Halo2VerifierReusable { } function z_evals(z, num_words_packed, perm_z_last_ptr, permutation_z_evals_ptr, theta_mptr, l_0, y, quotient_eval_numer) -> ret { - let num_words := and(num_words_packed, 0xFFFF) + let num_words := and(num_words_packed, PTR_BITMASK) // Initialize the free static memory pointer to store the column evals. mstore(0x20, 0x40) // Iterate through the tuple window length ( permutation_z_evals_len.len() - 1 ) offset by one word. @@ -202,7 +204,7 @@ contract Halo2VerifierReusable { let z_j := mload(next_z_ptr) quotient_eval_numer := addmod( mulmod(quotient_eval_numer, y, R), - mulmod(l_0, addmod(calldataload(and(z_j, 0xFFFF)), sub(R, calldataload(and(shr(32,z), 0xFFFF))), R), R), + mulmod(l_0, addmod(calldataload(and(z_j, PTR_BITMASK)), sub(R, calldataload(and(shr(32,z), PTR_BITMASK))), R), R), R ) col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) @@ -211,7 +213,7 @@ contract Halo2VerifierReusable { } // Due to the fact that permutation_columns.len() in H2 might not be divisible by permutation_chunk_len, the last column length might be less than permutation_chunk_len // We store this length in the last 16 bits of the num_words_packed word. - num_words := and(shr(16, num_words_packed), 0xFFFF) + num_words := and(shr(16, num_words_packed), PTR_BITMASK) col_evals(z, num_words, permutation_z_evals_ptr, theta_mptr) // iterate through col_evals to update the quotient_eval_numer accumulator let end_ptr := mload(0x20) @@ -221,57 +223,85 @@ contract Halo2VerifierReusable { ret := quotient_eval_numer } - function expression_operations(expressions_word, fsmp, acc) -> ret { - let mstore_ptr := add(fsmp, acc) - // Load in the least significant byte of the `expression` word to get the operation type - // Then determine which operation to peform and then store the result in the next available memory slot. - switch and(expressions_word, 0xFF) - // 0x00 => Advice/Fixed expression - case 0x00 { - expressions_word := shr(8, expressions_word) - // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. - mstore(mstore_ptr, calldataload(and(expressions_word, 0xFFFF))) - // Move to the next expression - expressions_word := shr(16, expressions_word) - } - // 0x01 => Negated expression - case 0x01 { - expressions_word := shr(8, expressions_word) - // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes - mstore(mstore_ptr, sub(R, mload(and(expressions_word, 0xFFFF)))) - // Move to the next expression - expressions_word := shr(16, expressions_word) - } - // 0x02 => Sum expression - case 0x02 { - expressions_word := shr(8, expressions_word) - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(mstore_ptr, addmod(mload(and(expressions_word, 0xFFFF)), mload(and(shr(16, expressions_word), 0xFFFF)),R)) - // Move to the next expression - expressions_word := shr(32, expressions_word) - } - // 0x03 => Product/scalar expression - case 0x03 { - expressions_word := shr(8, expressions_word) - // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes - // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes - mstore(mstore_ptr, mulmod(mload(and(expressions_word, 0xFFFF)),mload(and(shr(16, expressions_word), 0xFFFF)),R)) - // Move to the next expression - expressions_word := shr(32, expressions_word) + function lookup_input_accum(expressions_word, fsmp, i, code_ptr) -> ret0, ret1, ret2 { + expressions_word := shr(8, expressions_word) + // Number of words the mptr vars for the accumulator evaluations shifted up by one + let num_words_vars := mul(0x20, and(expressions_word, BYTE_FLAG_BITMASK)) + expressions_word := shr(8, expressions_word) + // initlaize the accumulator with the first value in the vars + let a := mload(and(expressions_word, PTR_BITMASK)) + expressions_word := shr(16, expressions_word) + let theta := mload(0x60) + let beta := mload(0x80) + for { let j } lt(j, num_words_vars) { j := add(j, 0x20) } { + for { } expressions_word { } { + a := addmod( + mulmod(a, theta, R), + mload(and(expressions_word, PTR_BITMASK)), + R + ) + expressions_word := shr(16, expressions_word) + } + ret0 := add(code_ptr, add(i, j)) + expressions_word := mload(ret0) } - ret := expressions_word + a := addmod(a, beta, R) + ret1 := expressions_word + ret2 := a } function expression_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { // Load in the least significant byte of the `expressions_word` word to get the total number of words we will need to load in. - let num_words_shift_up_one := add(mul(0x20, and(expressions_word, 0xFF)), 0x20) + let num_words_shift_up_one := add(mul(0x20, and(expressions_word, BYTE_FLAG_BITMASK)), 0x20) // start of the expression encodings expressions_word := shr(8, expressions_word) let acc for { let i := 0x20 } lt(i, num_words_shift_up_one) { i := add(i, 0x20) } { for { } expressions_word { } { - expressions_word := expression_operations(expressions_word, fsmp, acc) + let mstore_ptr := add(fsmp, acc) + // Load in the least significant byte of the `expression` word to get the operation type + // Then determine which operation to peform and then store the result in the next available memory slot. + switch and(expressions_word, BYTE_FLAG_BITMASK) + // 0x00 => Advice/Fixed expression + case 0x00 { + expressions_word := shr(8, expressions_word) + // Load the calldata ptr from the expression, which come from the 2nd and 3rd least significant bytes. + mstore(mstore_ptr, calldataload(and(expressions_word, PTR_BITMASK))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x01 => Negated expression + case 0x01 { + expressions_word := shr(8, expressions_word) + // Load the memory ptr from the expression, which come from the 2nd and 3rd least significant bytes + mstore(mstore_ptr, sub(R, mload(and(expressions_word, PTR_BITMASK)))) + // Move to the next expression + expressions_word := shr(16, expressions_word) + } + // 0x02 => Sum expression + case 0x02 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(mstore_ptr, addmod(mload(and(expressions_word, PTR_BITMASK)), mload(and(shr(16, expressions_word), PTR_BITMASK)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + // 0x03 => Product/scalar expression + case 0x03 { + expressions_word := shr(8, expressions_word) + // Load the lhs operand memory ptr from the expression, which comes from the 2nd and 3rd least significant bytes + // Load the rhs operand memory ptr from the expression, which comes from the 4th and 5th least significant bytes + mstore(mstore_ptr, mulmod(mload(and(expressions_word, PTR_BITMASK)),mload(and(shr(16, expressions_word), PTR_BITMASK)),R)) + // Move to the next expression + expressions_word := shr(32, expressions_word) + } + // 0x04 => (For lookup expressions) Start accumulator evaluations for the lookup (table or input) + // Will always occur at the end of the last word of the lookup expression. + case 0x04 { + ret0, ret1, ret2 := lookup_input_accum(expressions_word, fsmp, i, code_ptr) + leave + } acc := add(acc, 0x20) } ret0 := add(code_ptr, i) @@ -281,31 +311,18 @@ contract Halo2VerifierReusable { ret2 := sub(acc, 0x20) } - function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word, theta, beta) -> ret0, ret1 { - let idx - let vars // packed vars with ptrs to load in the vars stored in static memory in the + function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { // expression evaluation. - ret0, vars, idx := expression_evals_packed(fsmp, code_ptr, expressions_word) - ret1 := mload(and(vars, 0xFFFF)) // initlaize the accumulator with the first value in the vars - vars := shr(16, vars) - for { } vars { } { - ret1 := addmod( - mulmod(ret1, theta, R), - mload(and(vars, 0xFFFF)), - R - ) - vars := shr(16, vars) - } - ret1 := addmod(ret1, beta, R) + ret0, ret1, ret2 := expression_evals_packed(fsmp, code_ptr, expressions_word) } function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { // iterate through the input_tables_len let evals := mload(evals_ptr) // We store a boolean flag in the first LSG byte of the evals ptr to determine if we need to load in a new table or reuse the previous table. - let new_table := and(evals, 0xFF) + let new_table := and(evals, BYTE_FLAG_BITMASK) evals := shr(8, evals) - let phi := and(evals, 0xFFFF) + let phi := and(evals, PTR_BITMASK) quotient_eval_numer := addmod( mulmod(quotient_eval_numer, y, R), mulmod(mload(0x20),calldataload(phi), R), @@ -319,21 +336,18 @@ contract Halo2VerifierReusable { // load in the lookup_table_lines from the evals_ptr evals_ptr := add(evals_ptr, 0x20) // Due to the fact that lookups can share the previous table, we can cache it for reuse. - if new_table { - evals_ptr, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - } let input_expression := mload(evals_ptr) + if new_table { + evals_ptr, input_expression, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr)) + } // outer inputs len, stored in the first input expression word - let outer_inputs_len := and(input_expression, 0xFFFF) + let outer_inputs_len := and(input_expression, PTR_BITMASK) input_expression := shr(16, input_expression) // shift up the inputs iterator by the free static memory offset of 0xa0 for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { // call the expression_evals function to evaluate the input_lines let ident - evals_ptr, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression, mload(0x60), mload(0x80)) - evals_ptr := add(evals_ptr, 0x20) - input_expression := mload(evals_ptr) + evals_ptr, input_expression, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression) // store ident in free static memory mstore(j, ident) } @@ -369,12 +383,12 @@ contract Halo2VerifierReusable { } rhs := addmod( rhs, - sub(R, mulmod(calldataload(and(shr(32, evals), 0xFFFF)), tmp, R)), + sub(R, mulmod(calldataload(and(shr(32, evals), PTR_BITMASK)), tmp, R)), R ) lhs := mulmod( mulmod(table, tmp, R), - addmod(calldataload(and(shr(16, evals), 0xFFFF)), sub(R, calldataload(phi)), R), + addmod(calldataload(and(shr(16, evals), PTR_BITMASK)), sub(R, calldataload(phi)), R), R ) quotient_eval_numer := addmod( @@ -397,10 +411,10 @@ contract Halo2VerifierReusable { function point_rots(pcs_computations, pcs_ptr, word_shift, x_pow_of_omega, omega) -> ret0, ret1 { // Extract the 32 LSG bits (4 bytes) from the pcs_computations word to get the max rot - let values_max_rot := and(pcs_computations, 0xFFFFFFFF) - pcs_computations := shr(32, pcs_computations) + let values_max_rot := and(pcs_computations, BYTE_FLAG_BITMASK) + pcs_computations := shr(8, pcs_computations) for { let i := 0 } lt(i, values_max_rot) { i := add(i, 1) } { - let value := and(pcs_computations, 0xFFFF) + let value := and(pcs_computations, PTR_BITMASK) if not(eq(value, 0)) { mstore(value, x_pow_of_omega) } @@ -421,13 +435,13 @@ contract Halo2VerifierReusable { } function coeff_computations(coeff_len_data, coeff_data) -> ret { - let coeff_len := and(coeff_len_data, 0xFF) + let coeff_len := and(coeff_len_data, BYTE_FLAG_BITMASK) ret := shr(8, coeff_len_data) switch coeff_len case 0x01 { // We only encode the points if the coeff length is greater than 1. // Otherwise we just encode the mu_minus_point and coeff ptr. - mstore(and(shr(16, coeff_data), 0xFFFF), mod(mload(and(coeff_data, 0xFFFF)), R)) + mstore(and(shr(16, coeff_data), PTR_BITMASK), mod(mload(and(coeff_data, PTR_BITMASK)), R)) } default { let coeff @@ -435,22 +449,22 @@ contract Halo2VerifierReusable { for { let i := 0 } lt(i, coeff_len) { i := add(i, 1) } { let first := 0x01 let offset_base := mul(i, 16) - let point_i := mload(and(shr(offset_base, coeff_data), 0xFFFF)) + let point_i := mload(and(shr(offset_base, coeff_data), PTR_BITMASK)) for { let j:= 0 } lt(j, coeff_len) { j := add(j, 1) } { if eq(j, i) { continue } if first { - coeff := addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), 0xFFFF))), R) + coeff := addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), PTR_BITMASK))), R) first := 0 continue } - coeff := mulmod(coeff, addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), 0xFFFF))), R), R) + coeff := mulmod(coeff, addmod(point_i, sub(R, mload(and(shr(mul(j, 16), coeff_data), PTR_BITMASK))), R), R) } offset_base := add(offset_base, offset_aggr) - coeff := mulmod(coeff, mload(and(shr(offset_base, coeff_data), 0xFFFF)), R) + coeff := mulmod(coeff, mload(and(shr(offset_base, coeff_data), PTR_BITMASK)), R) offset_base := add(offset_base, offset_aggr) - mstore(and(shr(offset_base, coeff_data), 0xFFFF), coeff) + mstore(and(shr(offset_base, coeff_data), PTR_BITMASK), coeff) } } } @@ -458,26 +472,26 @@ contract Halo2VerifierReusable { function single_rot_set(r_evals_data, ptr, num_words, zeta, quotient_eval, coeff_ptr) -> ret0, ret1 { let coeff := mload(coeff_ptr) let r_eval - r_eval := addmod(r_eval, mulmod(coeff, calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_eval := addmod(r_eval, mulmod(coeff, calldataload(and(r_evals_data, PTR_BITMASK)), R), R) r_evals_data := shr(16, r_evals_data) r_eval := mulmod(r_eval, zeta, R) r_eval := addmod(r_eval, mulmod(coeff, quotient_eval, R), R) for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { for { } r_evals_data { } { - let eval_group_len := and(r_evals_data, 0xFF) + let eval_group_len := and(r_evals_data, BYTE_FLAG_BITMASK) r_evals_data := shr(8, r_evals_data) switch eq(eval_group_len, 0x0) case 0x0 { for { let j := 0 } lt(j, eval_group_len) { j := add(j, 1) } { - r_eval := addmod(mulmod(r_eval, zeta, R), mulmod(coeff, calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_eval := addmod(mulmod(r_eval, zeta, R), mulmod(coeff, calldataload(and(r_evals_data, PTR_BITMASK)), R), R) r_evals_data := shr(16, r_evals_data) } } default { for { - let mptr := and(r_evals_data, 0xFFFF) + let mptr := and(r_evals_data, PTR_BITMASK) r_evals_data := shr(16, r_evals_data) - let mptr_end := and(r_evals_data, 0xFFFF) + let mptr_end := and(r_evals_data, PTR_BITMASK) } lt(mptr_end, mptr) { mptr := sub(mptr, 0x20) } @@ -499,7 +513,7 @@ contract Halo2VerifierReusable { for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { for { } r_evals_data { } { for { let j := 0 } lt(j, rot_len) { j := add(j, 0x20) } { - r_eval := addmod(r_eval, mulmod(mload(add(coeff_ptr, j)), calldataload(and(r_evals_data, 0xFFFF)), R), R) + r_eval := addmod(r_eval, mulmod(mload(add(coeff_ptr, j)), calldataload(and(r_evals_data, PTR_BITMASK)), R), R) r_evals_data := shr(16, r_evals_data) } // Only on the last index do we NOT execute this if block. @@ -517,7 +531,7 @@ contract Halo2VerifierReusable { function r_evals_computation(rot_len, r_evals_data_ptr, zeta, quotient_eval, coeff_ptr) -> ret0, ret1 { let r_evals_data := mload(r_evals_data_ptr) // number of words to encode the data needed for this set in the r_evals computation. - let num_words := and(r_evals_data, 0xFF) + let num_words := and(r_evals_data, BYTE_FLAG_BITMASK) r_evals_data := shr(8, r_evals_data) switch rot_len case 0x20 { @@ -528,15 +542,15 @@ contract Halo2VerifierReusable { } function pairing_input_computations_first(len, pcs_ptr, data, theta_mptr, success) -> ret { - mstore(0x00, calldataload(and(data, 0xFFFF))) + mstore(0x00, calldataload(and(data, PTR_BITMASK))) data := shr(16, data) - mstore(0x20, calldataload(and(data, 0xFFFF))) + mstore(0x20, calldataload(and(data, PTR_BITMASK))) data := shr(16, data) for { let i := 0 } lt(i, len) { i := add(i, 0x20) } { for { } data { } { - let ptr_loc := and(data, 0xFF) + let ptr_loc := and(data, BYTE_FLAG_BITMASK) data := shr(8, data) - let comm_len := and(data, 0xFF) + let comm_len := and(data, BYTE_FLAG_BITMASK) data := shr(8, data) switch comm_len case 0x0 { @@ -544,9 +558,9 @@ contract Halo2VerifierReusable { case 0x00 { for { - let mptr := and(data, 0xFFFF) + let mptr := and(data, PTR_BITMASK) data := shr(16, data) - let mptr_end := and(data, 0xFFFF) + let mptr_end := and(data, PTR_BITMASK) } lt(mptr_end, mptr) { mptr := sub(mptr, 0x40) } @@ -558,9 +572,9 @@ contract Halo2VerifierReusable { case 0x01 { for { - let mptr := and(data, 0xFFFF) + let mptr := and(data, PTR_BITMASK) data := shr(16, data) - let mptr_end := and(data, 0xFFFF) + let mptr_end := and(data, PTR_BITMASK) } lt(mptr_end, mptr) { mptr := sub(mptr, 0x40) } @@ -574,21 +588,21 @@ contract Halo2VerifierReusable { switch ptr_loc case 0x00 { success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_acc(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + success := ec_add_acc(success, mload(and(data, PTR_BITMASK)), mload(and(shr(16,data), PTR_BITMASK))) if eq(comm_len, 0x02) { data := shr(32, data) success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_acc(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + success := ec_add_acc(success, mload(and(data, PTR_BITMASK)), mload(and(shr(16,data), PTR_BITMASK))) } data := shr(32, data) } case 0x01 { success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_acc(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + success := ec_add_acc(success, calldataload(and(data, PTR_BITMASK)), calldataload(and(shr(16,data), PTR_BITMASK))) if eq(comm_len, 0x02) { data := shr(32, data) success := ec_mul_acc(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_acc(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + success := ec_add_acc(success, calldataload(and(data, PTR_BITMASK)), calldataload(and(shr(16,data), PTR_BITMASK))) } data := shr(32, data) } @@ -606,15 +620,15 @@ contract Halo2VerifierReusable { } function pairing_input_computations(len, pcs_ptr, data, theta_mptr, success) -> ret { - mstore(0x80, calldataload(and(data, 0xFFFF))) + mstore(0x80, calldataload(and(data, PTR_BITMASK))) data := shr(16, data) - mstore(0xa0, calldataload(and(data, 0xFFFF))) + mstore(0xa0, calldataload(and(data, PTR_BITMASK))) data := shr(16, data) for { let i := 0 } lt(i, len) { i := add(i, 0x20) } { for { } data { } { - let ptr_loc := and(data, 0xFF) + let ptr_loc := and(data, BYTE_FLAG_BITMASK) data := shr(8, data) - let comm_len := and(data, 0xFF) + let comm_len := and(data, BYTE_FLAG_BITMASK) data := shr(8, data) switch comm_len case 0x0 { @@ -622,9 +636,9 @@ contract Halo2VerifierReusable { case 0x00 { for { - let mptr := and(data, 0xFFFF) + let mptr := and(data, PTR_BITMASK) data := shr(16, data) - let mptr_end := and(data, 0xFFFF) + let mptr_end := and(data, PTR_BITMASK) } lt(mptr_end, mptr) { mptr := sub(mptr, 0x40) } @@ -636,9 +650,9 @@ contract Halo2VerifierReusable { case 0x01 { for { - let mptr := and(data, 0xFFFF) + let mptr := and(data, PTR_BITMASK) data := shr(16, data) - let mptr_end := and(data, 0xFFFF) + let mptr_end := and(data, PTR_BITMASK) } lt(mptr_end, mptr) { mptr := sub(mptr, 0x40) } @@ -652,21 +666,21 @@ contract Halo2VerifierReusable { switch ptr_loc case 0x00 { success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_tmp(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + success := ec_add_tmp(success, mload(and(data, PTR_BITMASK)), mload(and(shr(16,data), PTR_BITMASK))) if eq(comm_len, 0x2) { data := shr(32, data) success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_tmp(success, mload(and(data, 0xFFFF)), mload(and(shr(16,data), 0xFFFF))) + success := ec_add_tmp(success, mload(and(data, PTR_BITMASK)), mload(and(shr(16,data), PTR_BITMASK))) } data := shr(32, data) } case 0x01 { success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_tmp(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + success := ec_add_tmp(success, calldataload(and(data, PTR_BITMASK)), calldataload(and(shr(16,data), PTR_BITMASK))) if eq(comm_len, 0x2) { data := shr(32, data) success := ec_mul_tmp(success, mload(add(theta_mptr, 0xA0))) - success := ec_add_tmp(success, calldataload(and(data, 0xFFFF)), calldataload(and(shr(16,data), 0xFFFF))) + success := ec_add_tmp(success, calldataload(and(data, PTR_BITMASK)), calldataload(and(shr(16,data), PTR_BITMASK))) } data := shr(32, data) } @@ -728,12 +742,12 @@ contract Halo2VerifierReusable { let challenge_len_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) let challenge_len_data := mload(challenge_len_ptr) - let num_words := and(challenge_len_data, 0xFF) + let num_words := and(challenge_len_data, BYTE_FLAG_BITMASK) challenge_len_data := shr(8, challenge_len_data) for { let i := 0 } lt(i, 1) { i := add(i, 1) } { for { } challenge_len_data { } { // add proof_cpt to num advices len - let proof_cptr_end := add(proof_cptr, and(challenge_len_data, 0xFFFF)) + let proof_cptr_end := add(proof_cptr, and(challenge_len_data, PTR_BITMASK)) challenge_len_data := shr(16, challenge_len_data) // Phase loop for { } lt(proof_cptr, proof_cptr_end) { } { @@ -743,7 +757,7 @@ contract Halo2VerifierReusable { challenge_mptr, hash_mptr := squeeze_challenge(challenge_mptr, hash_mptr) // Continue squeezing challenges based on num_challenges - let num_challenges := and(challenge_len_data, 0xFF) + let num_challenges := and(challenge_len_data, BYTE_FLAG_BITMASK) challenge_len_data := shr(8, challenge_len_data) for { let c := 1 } lt(c, num_challenges) { c := add(c, 1) } { challenge_mptr := squeeze_challenge_cont(challenge_mptr) @@ -944,7 +958,7 @@ contract Halo2VerifierReusable { let permutation_z_evals_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["permutation_computations_len_offset"]|hex() }}))) let permutation_z_evals := mload(permutation_z_evals_ptr) // Last idx of permutation evals == permutation_evals.len() - 1 - let last_idx := and(permutation_z_evals, 0xFF) + let last_idx := and(permutation_z_evals, BYTE_FLAG_BITMASK) permutation_z_evals := shr(8, permutation_z_evals) // Num of words scaled by 0x20 that take up each permutation eval (permutation_z_eval + column evals) // first and second LSG bytes contain the number of words for all of the permutation evals except the last. @@ -956,14 +970,14 @@ contract Halo2VerifierReusable { let l_0 := mload(add(theta_mptr, 0x200)) { // Get the first and second LSG bytes from the first permutation_z_evals word to load in (z, _, _) - let eval := addmod(l_0, sub(R, mulmod(l_0, calldataload(and(permutation_z_evals, 0xFFFF)), R)), R) + let eval := addmod(l_0, sub(R, mulmod(l_0, calldataload(and(permutation_z_evals, PTR_BITMASK)), R)), R) quotient_eval_numer := addmod(mulmod(quotient_eval_numer, y, R), eval, R) } { // Load in the last permutation_z_evals word - let perm_z_last_ptr := add(mul(last_idx, and(num_words, 0xFFFF)), permutation_z_evals_ptr) - let perm_z_last := calldataload(and(mload(perm_z_last_ptr), 0xFFFF)) + let perm_z_last_ptr := add(mul(last_idx, and(num_words, PTR_BITMASK)), permutation_z_evals_ptr) + let perm_z_last := calldataload(and(mload(perm_z_last_ptr), PTR_BITMASK)) quotient_eval_numer := addmod( mulmod(quotient_eval_numer, y, R), mulmod( @@ -1049,14 +1063,14 @@ contract Halo2VerifierReusable { let omega := mload(add(vk_mptr, {{ vk_const_offsets["omega"]|hex() }})) let omega_inv := mload(add(vk_mptr, {{ vk_const_offsets["omega_inv"]|hex() }})) let x_pow_of_omega := mulmod(x, omega, R) - x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 32, x_pow_of_omega, omega) + x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 8, x_pow_of_omega, omega) pcs_ptr := add(pcs_ptr, 0x20) point_computations := mload(pcs_ptr) - // Compute interm point - mstore(and(point_computations, 0xFFFF), x) + // Store interm point + mstore(and(point_computations, PTR_BITMASK), x) x_pow_of_omega := mulmod(x, omega_inv, R) point_computations := shr(16, point_computations) - x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 48, x_pow_of_omega, omega_inv) + x_pow_of_omega, pcs_ptr := point_rots(point_computations, pcs_ptr, 24, x_pow_of_omega, omega_inv) pcs_ptr := add(pcs_ptr, 0x20) pop(x_pow_of_omega) } @@ -1068,11 +1082,11 @@ contract Halo2VerifierReusable { mstore(0x20, 1) for { - let mptr := and(vanishing_computations, 0xFFFF) + let mptr := and(vanishing_computations, PTR_BITMASK) vanishing_computations := shr(16, vanishing_computations) - let mptr_end := and(vanishing_computations, 0xFFFF) + let mptr_end := and(vanishing_computations, PTR_BITMASK) vanishing_computations := shr(16, vanishing_computations) - let point_mptr := and(vanishing_computations, 0xFFFF) + let point_mptr := and(vanishing_computations, PTR_BITMASK) } lt(mptr, mptr_end) { @@ -1085,29 +1099,29 @@ contract Halo2VerifierReusable { pop(mu) vanishing_computations := shr(16, vanishing_computations) let s - s := mload(and(vanishing_computations, 0xFFFF)) + s := mload(and(vanishing_computations, PTR_BITMASK)) vanishing_computations := shr(16, vanishing_computations) for { } vanishing_computations { } { - s := mulmod(s, mload(and(vanishing_computations, 0xFFFF)), R) + s := mulmod(s, mload(and(vanishing_computations, PTR_BITMASK)), R) vanishing_computations := shr(16, vanishing_computations) } pcs_ptr := add(pcs_ptr, 0x20) vanishing_computations := mload(pcs_ptr) - mstore(and(vanishing_computations, 0xFFFF), s) + mstore(and(vanishing_computations, PTR_BITMASK), s) vanishing_computations := shr(16, vanishing_computations) let diff - let sets_len := and(vanishing_computations, 0xFFFF) + let sets_len := and(vanishing_computations, PTR_BITMASK) pcs_ptr := add(pcs_ptr, 0x20) vanishing_computations := mload(pcs_ptr) for { let i := 0 } lt(i, sets_len) { i := add(i, 1) } { - diff := mload(and(vanishing_computations, 0xFFFF)) + diff := mload(and(vanishing_computations, PTR_BITMASK)) vanishing_computations := shr(16, vanishing_computations) - for { } and(vanishing_computations, 0xFFFF) { } { - diff := mulmod(diff, mload(and(vanishing_computations, 0xFFFF)), R) + for { } and(vanishing_computations, PTR_BITMASK) { } { + diff := mulmod(diff, mload(and(vanishing_computations, PTR_BITMASK)), R) vanishing_computations := shr(16, vanishing_computations) } vanishing_computations := shr(16, vanishing_computations) - mstore(and(vanishing_computations, 0xFFFF), diff) + mstore(and(vanishing_computations, PTR_BITMASK), diff) if eq(i, 0) { mstore(0x00, diff) } @@ -1120,7 +1134,7 @@ contract Halo2VerifierReusable { let coeff_len_data := mload(pcs_ptr) // Load in the least significant byte of the `coeff_len_data` word to get the total number of words we will need to load in // that contains the packed Vec. - let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_len_data, 0xFF))) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_len_data, BYTE_FLAG_BITMASK))) coeff_len_data := shr(8, coeff_len_data) let i := pcs_ptr pcs_ptr := end_ptr_packed_lens @@ -1135,16 +1149,16 @@ contract Halo2VerifierReusable { // normalized_coeff_computations { let norm_coeff_data := mload(pcs_ptr) - success := batch_invert(success, 0, and(norm_coeff_data, 0xFFFF)) + success := batch_invert(success, 0, and(norm_coeff_data, PTR_BITMASK)) norm_coeff_data := shr(16, norm_coeff_data) let diff_0_inv := mload(0x00) - let mptr0 := and(norm_coeff_data, 0xFFFF) + let mptr0 := and(norm_coeff_data, PTR_BITMASK) norm_coeff_data := shr(16, norm_coeff_data) mstore(mptr0, diff_0_inv) for { let mptr := add(mptr0, 0x20) - let mptr_end := add(mptr0, and(norm_coeff_data, 0xFFFF)) + let mptr_end := add(mptr0, and(norm_coeff_data, PTR_BITMASK)) } lt(mptr, mptr_end) { mptr := add(mptr, 0x20) } @@ -1157,11 +1171,11 @@ contract Halo2VerifierReusable { // r_evals_computations { let r_evals_meta_data := mload(pcs_ptr) - let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(r_evals_meta_data, 0xFF))) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(r_evals_meta_data, BYTE_FLAG_BITMASK))) r_evals_meta_data := shr(8, r_evals_meta_data) - let set_coeff := and(r_evals_meta_data, 0xFFFF) + let set_coeff := and(r_evals_meta_data, PTR_BITMASK) r_evals_meta_data := shr(16, r_evals_meta_data) - let r_eval_mptr := and(r_evals_meta_data, 0xFFFF) + let r_eval_mptr := and(r_evals_meta_data, PTR_BITMASK) r_evals_meta_data := shr(16, r_evals_meta_data) let i := pcs_ptr pcs_ptr := end_ptr_packed_lens @@ -1171,8 +1185,8 @@ contract Halo2VerifierReusable { let r_eval for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { for { } r_evals_meta_data { } { - r_eval, pcs_ptr := r_evals_computation(and(r_evals_meta_data, 0xFF), pcs_ptr, zeta, quotient_eval, coeff_ptr) - coeff_ptr := add(coeff_ptr, and(r_evals_meta_data, 0xFF)) + r_eval, pcs_ptr := r_evals_computation(and(r_evals_meta_data, BYTE_FLAG_BITMASK), pcs_ptr, zeta, quotient_eval, coeff_ptr) + coeff_ptr := add(coeff_ptr, and(r_evals_meta_data, BYTE_FLAG_BITMASK)) r_evals_meta_data := shr(8, r_evals_meta_data) if not_first { r_eval := mulmod(r_eval, mload(set_coeff), R) @@ -1188,7 +1202,7 @@ contract Halo2VerifierReusable { // coeff_sums_computation { let coeff_sums_data := mload(pcs_ptr) - let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_sums_data, 0xFF))) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(coeff_sums_data, BYTE_FLAG_BITMASK))) coeff_sums_data := shr(8, coeff_sums_data) coeff_ptr := 0x20 let i := pcs_ptr @@ -1196,13 +1210,13 @@ contract Halo2VerifierReusable { for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { for { } coeff_sums_data { } { let sum := mload(coeff_ptr) - let len := and(coeff_sums_data, 0xFF) + let len := and(coeff_sums_data, BYTE_FLAG_BITMASK) coeff_sums_data := shr(8, coeff_sums_data) for { let j := 0x20 } lt(j, len) { j := add(j, 0x20) } { sum := addmod(sum, mload(add(coeff_ptr, j)), R) } coeff_ptr := add(coeff_ptr, len) - mstore(and(coeff_sums_data, 0xFFFF), sum) + mstore(and(coeff_sums_data, PTR_BITMASK), sum) coeff_sums_data := shr(16, coeff_sums_data) } coeff_sums_data := mload(add(i, 0x20)) @@ -1212,12 +1226,12 @@ contract Halo2VerifierReusable { // r_eval_computation { let r_eval_data := mload(pcs_ptr) - let mptr_end := and(r_eval_data, 0xFFFF) + let mptr_end := and(r_eval_data, PTR_BITMASK) for { let mptr := 0x00 r_eval_data := shr(16, r_eval_data) - let sum_mptr := and(r_eval_data, 0xFFFF) + let sum_mptr := and(r_eval_data, PTR_BITMASK) } lt(mptr, mptr_end) { @@ -1229,7 +1243,7 @@ contract Halo2VerifierReusable { } r_eval_data := shr(16, r_eval_data) success := batch_invert(success, 0, mptr_end) - let r_eval_ptr := and(r_eval_data, 0xFFFF) + let r_eval_ptr := and(r_eval_data, PTR_BITMASK) let r_eval := mulmod(mload(sub(mptr_end, 0x20)), mload(r_eval_ptr), R) r_eval_data := shr(16, r_eval_data) for @@ -1254,9 +1268,9 @@ contract Halo2VerifierReusable { let nu := mload(add(theta_mptr, 0xC0)) { let pairing_input_meta_data := mload(pcs_ptr) - let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(pairing_input_meta_data, 0xFF))) + let end_ptr_packed_lens := add(pcs_ptr, mul(0x20, and(pairing_input_meta_data, BYTE_FLAG_BITMASK))) pairing_input_meta_data := shr(8, pairing_input_meta_data) - let set_coeff := and(pairing_input_meta_data, 0xFFFF) + let set_coeff := and(pairing_input_meta_data, PTR_BITMASK) pairing_input_meta_data := shr(16, pairing_input_meta_data) let ec_points_cptr_packed := and(pairing_input_meta_data, 0xFFFFFFFFFFFFFFFFFFFF) pairing_input_meta_data := shr(80, pairing_input_meta_data) @@ -1265,7 +1279,7 @@ contract Halo2VerifierReusable { let first := 1 for { } lt(i, end_ptr_packed_lens) { i := add(i, 0x20) } { for { } pairing_input_meta_data { } { - let len := and(pairing_input_meta_data, 0xFF) + let len := and(pairing_input_meta_data, BYTE_FLAG_BITMASK) pairing_input_meta_data := shr(8, pairing_input_meta_data) if first { first := 0 @@ -1289,16 +1303,16 @@ contract Halo2VerifierReusable { mstore(0xa0, mload(add(vk_mptr, {{ vk_const_offsets["g1_y"]|hex() }}))) success := ec_mul_tmp(success, sub(R, mload(add(theta_mptr, 0x2A0)))) success := ec_add_acc(success, mload(0x80), mload(0xa0)) - mstore(0x80, calldataload(and(ec_points_cptr_packed, 0xFFFF))) + mstore(0x80, calldataload(and(ec_points_cptr_packed, PTR_BITMASK))) ec_points_cptr_packed := shr(16, ec_points_cptr_packed) - mstore(0xa0, calldataload(and(ec_points_cptr_packed, 0xFFFF))) + mstore(0xa0, calldataload(and(ec_points_cptr_packed, PTR_BITMASK))) ec_points_cptr_packed := shr(16, ec_points_cptr_packed) - success := ec_mul_tmp(success, sub(R, mload(and(ec_points_cptr_packed, 0xFFFF)))) + success := ec_mul_tmp(success, sub(R, mload(and(ec_points_cptr_packed, PTR_BITMASK)))) ec_points_cptr_packed := shr(16, ec_points_cptr_packed) success := ec_add_acc(success, mload(0x80), mload(0xa0)) - let w_prime_x := calldataload(and(ec_points_cptr_packed, 0xFFFF)) + let w_prime_x := calldataload(and(ec_points_cptr_packed, PTR_BITMASK)) ec_points_cptr_packed := shr(16, ec_points_cptr_packed) - let w_prime_y := calldataload(and(ec_points_cptr_packed, 0xFFFF)) + let w_prime_y := calldataload(and(ec_points_cptr_packed, PTR_BITMASK)) mstore(0x80, w_prime_x) mstore(0xa0, w_prime_y) success := ec_mul_tmp(success, mload(add(theta_mptr, 0xE0))) diff --git a/templates/Halo2VerifyingArtifact.sol b/templates/Halo2VerifyingArtifact.sol index cdb40aa..720490d 100644 --- a/templates/Halo2VerifyingArtifact.sol +++ b/templates/Halo2VerifyingArtifact.sol @@ -45,23 +45,14 @@ contract Halo2VerifyingArtifact { {%- for lookup in lookup_computations.lookups %} {%- let offset = base_offset + lookup.acc %} mstore({{ (32 * offset)|hex_padded(4) }}, {{ lookup.evals|hex_padded(64) }}) // lookup_evals[{{ loop.index0 }}] - {%- let table_inputs = lookup.table_inputs %} - {%- let branching_offset %} - {%- if let Some(table_inputs) = table_inputs %} {%- for table_line in lookup.table_lines %} mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] {%- endfor %} - mstore({{ (32 * (offset + 1 + lookup.table_lines.len()))|hex_padded(4) }}, {{ table_inputs|hex_padded(64) }}) // lookup_table_inputs [{{ loop.index0 }}] - {%- let branching_offset = offset + 1 + lookup.table_lines.len() %} - {%- else %} - {%- let branching_offset = offset %} - {%- endif %} {%- for input in lookup.inputs %} - {%- let offset = branching_offset + loop.index0 + input.acc %} + {%- let offset = offset + 1 + lookup.table_lines.len() + loop.index0 + input.acc %} {%- for expression in input.expression %} - mstore({{ (32 * (offset + loop.index0 + 1))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] + mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] {%- endfor %} - mstore({{ (32 * (offset + input.expression.len() + 1))|hex_padded(4) }}, {{ input.vars|hex_padded(64) }}) // input_vars [{{ loop.index0 }}] {%- endfor %} {%- endfor %} {%- let offset_8 = offset_7 + lookup_computations.len() %} From b101da40e78b35627c771e1f150ca0d6925a7045 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 25 Aug 2024 20:12:58 -0300 Subject: [PATCH 56/65] *optimization: enumerate set diff ptr at run time. --- src/codegen/pcs.rs | 45 +++++++++++++++++------------ templates/Halo2VerifierReusable.sol | 28 ++++++++++-------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/codegen/pcs.rs b/src/codegen/pcs.rs index 06ce59e..72d3c87 100644 --- a/src/codegen/pcs.rs +++ b/src/codegen/pcs.rs @@ -773,25 +773,36 @@ pub(crate) fn bdfg21_computations_dynamic( }; let vanishing_computations: Vec = { - let pack_mptrs_and_s_ptrs = { + let pack_mptrs_and_s_ptrs: Vec = { let mptr = mu_minus_points.first_key_value().unwrap().1.ptr(); let mptr_word = U256::from(mptr.value().as_usize()); let mptr_end = mptr + mu_minus_points.len(); let mptr_end_word = U256::from(mptr_end.value().as_usize()); - let mut packed_word = U256::from(0); + let mut last_idx = 0; + let mut packed_words = vec![U256::from(0)]; // start packing the mptrs - packed_word |= mptr_word; - packed_word |= mptr_end_word << 16; - packed_word |= U256::from(free_mptr.value().as_usize()) << 32; - let mut offset = 48; + packed_words[0] |= mptr_word; + packed_words[0] |= mptr_end_word << 16; + packed_words[0] |= U256::from(free_mptr.value().as_usize()) << 32; + // bit offset length to where the number of words allocated to the s_ptrs will be stored. + let words_alloc_offset = 48; + let mut bit_counter = words_alloc_offset + 8; // start packing the s_ptrs sets[0].rots().iter().for_each(|rot| { // panic if offset is exceeds 256 - assert!(offset <= 256); - packed_word |= U256::from(mu_minus_points[rot].ptr().value().as_usize()) << offset; - offset += 16; + let next_bit_counter = bit_counter + 16; + if next_bit_counter > 256 { + last_idx += 1; + packed_words.push(U256::from(0)); + bit_counter = 0; + } + packed_words[last_idx] |= + U256::from(mu_minus_points[rot].ptr().value().as_usize()) << bit_counter; + bit_counter += 16; }); - packed_word + // store the num words allocated for s_ptrs + packed_words[0] |= U256::from(last_idx + 1) << words_alloc_offset; + packed_words }; let pack_vanishing_0_and_sets_len: ruint::Uint<256, 4> = { let vanishing_0_word = U256::from(vanishing_0.ptr().value().as_usize()); @@ -802,8 +813,8 @@ pub(crate) fn bdfg21_computations_dynamic( packed_word }; let pack_set_diffs_words: Vec> = { - izip!(&sets, &diffs) - .map(|(set, diff)| { + sets.iter() + .map(|set| { let mut packed_word = U256::from(0); let mut offset = 0; set.diffs().iter().for_each(|rot| { @@ -816,11 +827,6 @@ pub(crate) fn bdfg21_computations_dynamic( packed_word |= U256::from(0x20) << offset; offset += 16; } - // pack a blank ptr - packed_word |= U256::from(0) << offset; - offset += 16; - // pack the diff.ptr() - packed_word |= U256::from(diff.ptr().value().as_usize()) << offset; assert!( offset <= 256, "The offset for packing the set diff word exceeds 256 bits", @@ -830,7 +836,8 @@ pub(crate) fn bdfg21_computations_dynamic( .collect_vec() }; chain!( - [pack_mptrs_and_s_ptrs, pack_vanishing_0_and_sets_len], + pack_mptrs_and_s_ptrs.into_iter(), + [pack_vanishing_0_and_sets_len], pack_set_diffs_words.into_iter() ) .collect_vec() @@ -1122,7 +1129,7 @@ pub(crate) fn bdfg21_computations_dynamic( let offset = 24; let next_bit_counter = bit_counter + offset; let len = coeffs.len() * 32; - assert!(len < 256, "The length of the coeffs exceeds 256 bits",); + assert!(len < 256, "The length of the coeffs exceeds 256 bits"); if next_bit_counter > 256 { last_idx += 1; packed_words.push(U256::from(0)); diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index ffa2d00..e082b3a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -744,7 +744,7 @@ contract Halo2VerifierReusable { let challenge_len_data := mload(challenge_len_ptr) let num_words := and(challenge_len_data, BYTE_FLAG_BITMASK) challenge_len_data := shr(8, challenge_len_data) - for { let i := 0 } lt(i, 1) { i := add(i, 1) } { + for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { for { } challenge_len_data { } { // add proof_cpt to num advices len let proof_cptr_end := add(proof_cptr, and(challenge_len_data, PTR_BITMASK)) @@ -1098,16 +1098,20 @@ contract Halo2VerifierReusable { } pop(mu) vanishing_computations := shr(16, vanishing_computations) - let s - s := mload(and(vanishing_computations, PTR_BITMASK)) + let num_words := and(vanishing_computations, BYTE_FLAG_BITMASK) + vanishing_computations := shr(8, vanishing_computations) + let s := mload(and(vanishing_computations, PTR_BITMASK)) vanishing_computations := shr(16, vanishing_computations) - for { } vanishing_computations { } { - s := mulmod(s, mload(and(vanishing_computations, PTR_BITMASK)), R) - vanishing_computations := shr(16, vanishing_computations) + for { let i } lt(i, num_words) { i := add(i, 1) } { + for { } vanishing_computations { } { + s := mulmod(s, mload(and(vanishing_computations, PTR_BITMASK)), R) + vanishing_computations := shr(16, vanishing_computations) + } + pcs_ptr := add(pcs_ptr, 0x20) + vanishing_computations := mload(pcs_ptr) } - pcs_ptr := add(pcs_ptr, 0x20) - vanishing_computations := mload(pcs_ptr) - mstore(and(vanishing_computations, PTR_BITMASK), s) + let diff_ptr := and(vanishing_computations, PTR_BITMASK) + mstore(diff_ptr, s) vanishing_computations := shr(16, vanishing_computations) let diff let sets_len := and(vanishing_computations, PTR_BITMASK) @@ -1116,12 +1120,12 @@ contract Halo2VerifierReusable { for { let i := 0 } lt(i, sets_len) { i := add(i, 1) } { diff := mload(and(vanishing_computations, PTR_BITMASK)) vanishing_computations := shr(16, vanishing_computations) - for { } and(vanishing_computations, PTR_BITMASK) { } { + for { } vanishing_computations { } { diff := mulmod(diff, mload(and(vanishing_computations, PTR_BITMASK)), R) vanishing_computations := shr(16, vanishing_computations) } - vanishing_computations := shr(16, vanishing_computations) - mstore(and(vanishing_computations, PTR_BITMASK), diff) + diff_ptr := add(0x20, diff_ptr) + mstore(diff_ptr, diff) if eq(i, 0) { mstore(0x00, diff) } From 659ad43e45240c759403207c48fa827ec7b4af2e Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:04:23 -0400 Subject: [PATCH 57/65] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0ae2f1f..456cd56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", rev = "38abade26078496aa4d8738b967b0e7fa5d9505c" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2", rev = "d9d470ce8c8e64293c0e678bb3c99685497d0b08" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" From b58b241dc40d6af878021bc86abc768fb13f2f8a Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:22:08 -0400 Subject: [PATCH 58/65] fix: patch tests --- Cargo.toml | 5 ++++- src/test.rs | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 456cd56..d162b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", rev = "d9d470ce8c8e64293c0e678bb3c99685497d0b08" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2?branch=ac/cache-lookup-commitments#8b13a0d2a7a34d8daab010dadb2c47dfa47d37d0", package = "halo2_proofs", branch = "ac/cache-lookup-commitments" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" @@ -31,3 +31,6 @@ mv-lookup = ["halo2_proofs/mv-lookup", "halo2_maingate/mv-lookup"] [[example]] name = "separately" required-features = ["evm"] + +[patch.'https://github.com/zkonduit/halo2'] +halo2_proofs = { git = "https://github.com/zkonduit/halo2?branch=ac/cache-lookup-commitments#8b13a0d2a7a34d8daab010dadb2c47dfa47d37d0", package = "halo2_proofs", branch = "ac/cache-lookup-commitments" } diff --git a/src/test.rs b/src/test.rs index 8308753..265bd14 100644 --- a/src/test.rs +++ b/src/test.rs @@ -165,7 +165,7 @@ mod halo2 { pub fn create_testdata_bdfg21>( k: u32, acc_encoding: Option, - mut rng: impl RngCore + Clone, + mut rng: impl RngCore + Clone + Sync + Send, ) -> ( ParamsKZG, VerifyingKey, @@ -216,7 +216,8 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::Base: PrimeField>, + ::Base: + PrimeField>, M::Fr: PrimeField>, { let s = M::Fr::random(&mut rng); @@ -257,7 +258,7 @@ mod halo2 { F1: PrimeField>, F2: PrimeField>, { - let big = U256::from_le_bytes(fe.borrow().to_repr()); + let big = U256::from_le_bytes(fe.borrow().to_repr().inner().clone()); let mask = &((U256::from(1) << num_limb_bits) - U256::from(1)); (0usize..) .step_by(num_limb_bits) @@ -271,7 +272,7 @@ mod halo2 { F: PrimeField>, { let bytes = u256.borrow().to_le_bytes::<32>(); - F::from_repr_vartime(bytes).unwrap() + F::from_repr_vartime(bytes.into()).unwrap() } pub mod huge { @@ -303,8 +304,10 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField>, - ::Base: PrimeField>, + ::ScalarExt: + PrimeField>, + ::Base: + PrimeField>, M::Fr: PrimeField>, { fn min_k() -> u32 { @@ -538,8 +541,10 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField>, - ::Base: PrimeField>, + ::ScalarExt: + PrimeField>, + ::Base: + PrimeField>, M::Fr: PrimeField>, { fn min_k() -> u32 { From a830dcf7a4fd5e9af74b55a50b279b8a8f31ae7c Mon Sep 17 00:00:00 2001 From: dante <45801863+alexander-camuto@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:42:54 -0400 Subject: [PATCH 59/65] fix: example --- examples/separately.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/separately.rs b/examples/separately.rs index 89c3ed8..3854016 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -76,7 +76,7 @@ fn create_proof_checked( pk: &ProvingKey, circuit: impl Circuit, instances: &[Fr], - mut rng: impl RngCore, + mut rng: impl RngCore + Send + Sync, ) -> Vec { use halo2_proofs::{ poly::kzg::{ From b544e4b1ca3a0456fd3483dca6b1cd559e6780af Mon Sep 17 00:00:00 2001 From: Ethan Date: Sat, 31 Aug 2024 00:10:38 -0400 Subject: [PATCH 60/65] h2 curve update + revm version bump --- Cargo.toml | 10 ++++--- examples/separately.rs | 2 +- rust-toolchain | 2 +- src/codegen/evaluator.rs | 6 ++-- src/codegen/template.rs | 5 ---- src/codegen/util.rs | 9 +++--- src/evm.rs | 59 +++++++++++++++++----------------------- src/test.rs | 44 ++++++++++++++++-------------- src/transcript.rs | 26 ++++++++++-------- 9 files changed, 78 insertions(+), 85 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c28973..d80bed2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,23 +4,22 @@ version = "0.1.0" edition = "2021" [dependencies] -halo2_proofs = { git = "https://github.com/zkonduit/halo2", branch = "main" } +halo2_proofs = { git = "https://github.com/zkonduit/halo2?branch=ac/cache-lookup-commitments#8b13a0d2a7a34d8daab010dadb2c47dfa47d37d0", package = "halo2_proofs", branch = "ac/cache-lookup-commitments" } askama = { version = "0.12.0", features = ["config"], default-features = false } hex = "0.4.3" ruint = "1.8.0" sha3 = "0.10" itertools = "0.11.0" -regex = { version = "1", default_features = false } # Remove when `vk.transcript_repr()` is ready for usage. blake2b_simd = "1" # For feature = "evm" -revm = { version = "3.3.0", default-features = false, optional = true } +revm = { version = "14.0.1", default-features = false, optional = true } [dev-dependencies] rand = "0.8.5" -revm = { version = "3.3.0", default-features = false } +revm = { version = "14.0.1", default-features = false } halo2_maingate = { git = "https://github.com/zkonduit/halo2wrong", branch = "ac/chunked-mv-lookup", package = "maingate" } @@ -32,3 +31,6 @@ mv-lookup = ["halo2_proofs/mv-lookup", "halo2_maingate/mv-lookup"] [[example]] name = "separately" required-features = ["evm"] + +[patch.'https://github.com/zkonduit/halo2'] +halo2_proofs = { git = "https://github.com/zkonduit/halo2?branch=ac/cache-lookup-commitments#8b13a0d2a7a34d8daab010dadb2c47dfa47d37d0", package = "halo2_proofs", branch = "ac/cache-lookup-commitments" } diff --git a/examples/separately.rs b/examples/separately.rs index 5cc06b1..0e5607e 100644 --- a/examples/separately.rs +++ b/examples/separately.rs @@ -76,7 +76,7 @@ fn create_proof_checked( pk: &ProvingKey, circuit: impl Circuit, instances: &[Fr], - mut rng: impl RngCore, + mut rng: impl RngCore + Send + Sync, ) -> Vec { use halo2_proofs::{ poly::kzg::{ diff --git a/rust-toolchain b/rust-toolchain index fb0f946..6ae3510 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,2 +1,2 @@ [toolchain] -channel = "1.77.2" \ No newline at end of file +channel = "nightly-2024-07-18" diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 41068a6..7115d95 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -26,7 +26,7 @@ pub(crate) struct EvaluatorStatic<'a, F: PrimeField> { impl<'a, F> EvaluatorStatic<'a, F> where - F: PrimeField, + F: PrimeField>, { pub(crate) fn new( cs: &'a ConstraintSystem, @@ -583,7 +583,7 @@ pub(crate) struct EvaluatorDynamic<'a, F: PrimeField> { impl<'a, F> EvaluatorDynamic<'a, F> where - F: PrimeField, + F: PrimeField>, { pub(crate) fn new( cs: &'a ConstraintSystem, @@ -1203,7 +1203,7 @@ fn evaluate( scaled: &impl Fn(T, U256) -> T, ) -> T where - F: PrimeField, + F: PrimeField>, { let evaluate = |expr: &Expression| { evaluate( diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 9ba1223..79983ed 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -11,11 +11,6 @@ use super::{ evaluator::{GateDataEncoded, LookupsDataEncoded, PermutationDataEncoded}, pcs::PcsDataEncoded, }; -// Renderable trait for rendering logic -pub(crate) trait Renderable { - fn render(&self, writer: &mut dyn fmt::Write) -> Result<(), fmt::Error>; -} - #[derive(Template)] #[template(path = "Halo2VerifyingKey.sol")] pub(crate) struct Halo2VerifyingKey { diff --git a/src/codegen/util.rs b/src/codegen/util.rs index 95bb049..e327101 100644 --- a/src/codegen/util.rs +++ b/src/codegen/util.rs @@ -911,15 +911,16 @@ pub(crate) fn fr_to_u256(fe: impl Borrow) -> U256 { pub(crate) fn fe_to_u256(fe: impl Borrow) -> U256 where - F: PrimeField, + F: PrimeField>, { - U256::from_le_bytes(fe.borrow().to_repr()) + #[allow(clippy::clone_on_copy)] + U256::from_le_bytes(fe.borrow().to_repr().inner().clone()) } #[cfg(feature = "mv-lookup")] pub(crate) fn expression_consts(cs: &ConstraintSystem) -> Vec where - F: PrimeField, + F: PrimeField>, { fn collect_constants(expression: &Expression, constants: &mut Vec) { match expression { @@ -975,7 +976,7 @@ where #[cfg(not(feature = "mv-lookup"))] pub(crate) fn expression_consts(cs: &ConstraintSystem) -> Vec where - F: PrimeField, + F: PrimeField>, { fn collect_constants(expression: &Expression, constants: &mut Vec) { match expression { diff --git a/src/evm.rs b/src/evm.rs index fe9fcc0..6142c00 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -51,9 +51,9 @@ pub(crate) mod test { pub use revm; use revm::{ primitives::{ - Address, CfgEnv, CreateScheme, Env, ExecutionResult, Output, TransactTo, TxEnv, + Address, CfgEnv, CfgEnvWithHandlerCfg, ExecutionResult, Output, TransactTo, TxEnv, }, - InMemoryDB, EVM, + Evm as EVM, InMemoryDB, }; use std::{ fmt::{self, Debug, Formatter}, @@ -108,38 +108,30 @@ pub(crate) mod test { } /// Evm runner. - pub struct Evm { - evm: EVM, + pub struct Evm<'a> { + evm: EVM<'a, (), InMemoryDB>, } - impl Debug for Evm { + impl Debug for Evm<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut debug_struct = f.debug_struct("Evm"); - debug_struct - .field("env", &self.evm.env) - .field("db", &self.evm.db.as_ref().unwrap()) - .finish() + self.evm.fmt(f) } } - impl Default for Evm { + impl Default for Evm<'_> { fn default() -> Self { - Self { - evm: EVM { - env: Default::default(), - db: Some(Default::default()), - }, - } + let evm = EVM::builder().with_db(InMemoryDB::default()).build(); + Self { evm } } } - impl Evm { + impl Evm<'_> { /// Return code_size of given address. /// /// # Panics /// Panics if given address doesn't have bytecode. pub fn code_size(&mut self, address: Address) -> usize { - self.evm.db.as_ref().unwrap().accounts[&address] + self.evm.db().accounts[&address] .info .code .as_ref() @@ -149,17 +141,16 @@ pub(crate) mod test { /// Return a version of the evm that allows for unlimited deployments sizes. pub fn unlimited() -> Self { - let mut cfg: CfgEnv = Default::default(); - cfg.limit_contract_code_size = Some(usize::MAX); - Self { - evm: EVM { - env: Env { - cfg, - ..Default::default() - }, - db: Some(Default::default()), - }, - } + let mut cfg_env: CfgEnv = Default::default(); + cfg_env.limit_contract_code_size = Some(usize::MAX); + let evm = EVM::builder() + .with_db(InMemoryDB::default()) + .with_cfg_env_with_handler_cfg(CfgEnvWithHandlerCfg { + cfg_env, + handler_cfg: Default::default(), + }) + .build(); + Self { evm } } /// Apply create transaction with given `bytecode` as creation bytecode. @@ -170,7 +161,7 @@ pub(crate) mod test { pub fn create(&mut self, bytecode: Vec) -> (Address, u64) { let (gas_used, output) = self.transact_success_or_panic(TxEnv { gas_limit: u64::MAX, - transact_to: TransactTo::Create(CreateScheme::Create), + transact_to: TransactTo::Create, data: bytecode.into(), ..Default::default() }); @@ -199,9 +190,9 @@ pub(crate) mod test { } fn transact_success_or_panic(&mut self, tx: TxEnv) -> (u64, Output) { - self.evm.env.tx = tx; + self.evm.context.evm.env.tx = tx; let result = self.evm.transact_commit().unwrap(); - self.evm.env.tx = Default::default(); + self.evm.context.evm.env.tx = Default::default(); match result { ExecutionResult::Success { gas_used, @@ -213,7 +204,7 @@ pub(crate) mod test { println!("--- logs from {} ---", logs[0].address); for (log_idx, log) in logs.iter().enumerate() { println!("log#{log_idx}"); - for (topic_idx, topic) in log.topics.iter().enumerate() { + for (topic_idx, topic) in log.topics().iter().enumerate() { println!(" topic{topic_idx}: {topic:?}"); } } diff --git a/src/test.rs b/src/test.rs index 1189e96..d1f18a7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -81,7 +81,7 @@ fn run_render_separately>() { let verifier_creation_code = compile_solidity(&verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); - let mut evm = Evm::unlimited(); + let mut evm = Evm::default(); let (verifier_address, _gas_cost) = evm.create(verifier_creation_code); let verifier_runtime_code_size = evm.code_size(verifier_address); @@ -177,7 +177,7 @@ mod halo2 { pub fn create_testdata_bdfg21>( k: u32, acc_encoding: Option, - mut rng: impl RngCore + Clone, + mut rng: impl RngCore + Clone + Sync + Send, ) -> ( ParamsKZG, VerifyingKey, @@ -228,8 +228,9 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::Base: PrimeField, - M::Fr: PrimeField, + ::Base: + PrimeField>, + M::Fr: PrimeField>, { let s = M::Fr::random(&mut rng); let g1 = M::G1Affine::generator(); @@ -254,8 +255,8 @@ mod halo2 { fn ec_point_to_limbs(ec_point: impl Borrow, num_limb_bits: usize) -> Vec where C: CurveAffine, - C::Base: PrimeField, - C::Scalar: PrimeField, + C::Base: PrimeField>, + C::Scalar: PrimeField>, { let coords = ec_point.borrow().coordinates().unwrap(); [*coords.x(), *coords.y()] @@ -266,10 +267,11 @@ mod halo2 { fn fe_to_limbs(fe: impl Borrow, num_limb_bits: usize) -> Vec where - F1: PrimeField, - F2: PrimeField, + F1: PrimeField>, + F2: PrimeField>, { - let big = U256::from_le_bytes(fe.borrow().to_repr()); + #[allow(clippy::clone_on_copy)] + let big = U256::from_le_bytes(fe.borrow().to_repr().inner().clone()); let mask = &((U256::from(1) << num_limb_bits) - U256::from(1)); (0usize..) .step_by(num_limb_bits) @@ -280,10 +282,10 @@ mod halo2 { fn fe_from_u256(u256: impl Borrow) -> F where - F: PrimeField, + F: PrimeField>, { let bytes = u256.borrow().to_le_bytes::<32>(); - F::from_repr_vartime(bytes).unwrap() + F::from_repr_vartime(bytes.into()).unwrap() } pub mod huge { @@ -315,9 +317,11 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField, - ::Base: PrimeField, - M::Fr: PrimeField, + ::ScalarExt: + PrimeField>, + ::Base: + PrimeField>, + M::Fr: PrimeField>, { fn min_k() -> u32 { 6 @@ -351,8 +355,6 @@ mod halo2 { Column, ); type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); fn without_witnesses(&self) -> Self { unimplemented!() @@ -550,9 +552,11 @@ mod halo2 { where M: MultiMillerLoop, M::G1Affine: CurveAffine, - ::ScalarExt: PrimeField, - ::Base: PrimeField, - M::Fr: PrimeField, + ::ScalarExt: + PrimeField>, + ::Base: + PrimeField>, + M::Fr: PrimeField>, { fn min_k() -> u32 { 9 @@ -580,8 +584,6 @@ mod halo2 { { type Config = MainGateWithRangeConfig; type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "halo2_circuit_params")] - type Params = (); fn without_witnesses(&self) -> Self { unimplemented!() diff --git a/src/transcript.rs b/src/transcript.rs index f125329..346404c 100644 --- a/src/transcript.rs +++ b/src/transcript.rs @@ -37,12 +37,12 @@ impl Keccak256Transcript { 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; 0x20]; @@ -58,7 +58,7 @@ where impl Transcript> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn squeeze_challenge(&mut self) -> ChallengeEvm { let buf_len = self.buf.len(); @@ -95,7 +95,7 @@ where impl TranscriptRead> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn read_point(&mut self) -> io::Result { let mut reprs = [::Repr::default(); 2]; @@ -121,7 +121,7 @@ where let mut data = [0; 0x20]; self.stream.read_exact(data.as_mut())?; data.reverse(); - let scalar = C::Scalar::from_repr_vartime(data) + let scalar = C::Scalar::from_repr_vartime(data.into()) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Invalid scalar".to_string()))?; Transcript::>::common_scalar(self, scalar)?; Ok(scalar) @@ -131,7 +131,7 @@ where impl TranscriptReadBuffer> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(reader: R) -> Self { Keccak256Transcript::new(reader) @@ -141,7 +141,7 @@ where impl TranscriptWrite> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn write_point(&mut self, ec_point: C) -> io::Result<()> { self.common_point(ec_point)?; @@ -165,7 +165,7 @@ where impl TranscriptWriterBuffer> for Keccak256Transcript where C: CurveAffine, - C::Scalar: PrimeField, + C::Scalar: PrimeField>, { fn init(writer: W) -> Self { Keccak256Transcript::new(writer) @@ -178,15 +178,17 @@ where fn u256_to_fe(value: U256) -> F where - F: PrimeField, + F: PrimeField>, { let value = value % modulus::(); - F::from_repr(value.to_le_bytes::<0x20>()).unwrap() + F::from_repr(value.to_le_bytes::<0x20>().into()).unwrap() } fn modulus() -> U256 where - F: PrimeField, + F: PrimeField>, { - U256::from_le_bytes((-F::ONE).to_repr()) + U256::from(1) + #[allow(clippy::clone_on_copy)] + let upper = U256::from_le_bytes((-F::ONE).to_repr().inner().clone()); + upper + U256::from(1) } From d600b141bdd241f6af07970c119bfbdf3418c2ef Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 4 Sep 2024 21:35:29 -0500 Subject: [PATCH 61/65] *load in all challenge related constants before performing challenge evals. --- templates/Halo2VerifierReusable.sol | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index e082b3a..b391d2a 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -721,6 +721,14 @@ contract Halo2VerifierReusable { let num_instances := mload(add(vk_mptr,{{ vk_const_offsets["num_instances"]|hex() }})) success := and(success, eq(num_instances, calldataload(sub(instance_cptr,0x20)))) + let proof_cptr := PROOF_CPTR + let num_evals := mul(0x20, mload(add(vk_mptr, {{ vk_const_offsets["num_evals"]|hex() }}))) + + let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory + // Set the theta_mptr (vk_mptr + vk_len + challenges_length) + theta_mptr := add(challenge_mptr, mload(add(vk_mptr, {{ vk_const_offsets["challenges_offset"]|hex() }}))) + let challenge_len_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) + // Read instances and witness commitments and generate challenges let hash_mptr := 0x20 for @@ -734,12 +742,6 @@ contract Halo2VerifierReusable { instance_cptr := add(instance_cptr, 0x20) hash_mptr := add(hash_mptr, 0x20) } - - let proof_cptr := PROOF_CPTR - let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory - // Set the theta_mptr (vk_mptr + vk_len + challenges_length) - theta_mptr := add(challenge_mptr, mload(add(vk_mptr, {{ vk_const_offsets["challenges_offset"]|hex() }}))) - let challenge_len_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) let challenge_len_data := mload(challenge_len_ptr) let num_words := and(challenge_len_data, BYTE_FLAG_BITMASK) @@ -769,7 +771,7 @@ contract Halo2VerifierReusable { // Read evaluations for - { let proof_cptr_end := add(proof_cptr, mul(0x20, mload(add(vk_mptr, {{ vk_const_offsets["num_evals"]|hex() }})))) } // num_evals + { let proof_cptr_end := add(proof_cptr, num_evals) } // num_evals lt(proof_cptr, proof_cptr_end) {} { @@ -795,7 +797,7 @@ contract Halo2VerifierReusable { // TODO {%- endmatch %} - // Copy full vk into memory + // Copy full vk into memory (some parts were overwritten during the challenge generation) extcodecopy(vk, vk_mptr, 0x00, vk_len) // Read accumulator from instances From d1464f901046ad0641c8a0b72e1b8bcedc406d0b Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 5 Sep 2024 20:04:37 -0500 Subject: [PATCH 62/65] *reduce `extcodecopy` call by one by only loading in vka data needed for challenges --- src/codegen.rs | 229 ++++++++++++++++++--------- src/codegen/template.rs | 2 - templates/Halo2VerifierReusable.sol | 53 ++++--- templates/Halo2VerifyingArtifact.sol | 6 +- 4 files changed, 185 insertions(+), 105 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 3d32a78..ece173e 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -31,6 +31,20 @@ pub(crate) mod util; pub use pcs::BatchOpenScheme; +// Maximum capacity of 10 words allocated for the num_advices_user_challenges encoded data. +const NUM_ADVICES_USER_CHALLENGES_LABELS: [&str; 10] = [ + "num_advices_user_challenges_0", + "num_advices_user_challenges_1", + "num_advices_user_challenges_2", + "num_advices_user_challenges_3", + "num_advices_user_challenges_4", + "num_advices_user_challenges_5", + "num_advices_user_challenges_6", + "num_advices_user_challenges_7", + "num_advices_user_challenges_8", + "num_advices_user_challenges_9", +]; + /// Solidity verifier generator for [`halo2`] proof with KZG polynomial commitment scheme on BN254. #[derive(Debug)] pub struct SolidityGenerator<'a> { @@ -171,74 +185,108 @@ impl<'a> SolidityGenerator<'a> { Ok((verifier_output, vk_output)) } - fn dummy_vk_constants(separate: bool) -> Vec<(&'static str, U256)> { - if separate { - vec![ - ("vk_digest", U256::from(0)), - ("vk_mptr", U256::from(0)), - ("vk_len", U256::from(0)), - ("num_instances", U256::from(0)), - ("k", U256::from(0)), - ("n_inv", U256::from(0)), - ("omega", U256::from(0)), - ("omega_inv", U256::from(0)), - ("omega_inv_to_l", U256::from(0)), - ("has_accumulator", U256::from(0)), - ("acc_offset", U256::from(0)), - ("num_acc_limbs", U256::from(0)), - ("num_acc_limb_bits", U256::from(0)), - ("g1_x", U256::from(0)), - ("g1_y", U256::from(0)), - ("g2_x_1", U256::from(0)), - ("g2_x_2", U256::from(0)), - ("g2_y_1", U256::from(0)), - ("g2_y_2", U256::from(0)), - ("neg_s_g2_x_1", U256::from(0)), - ("neg_s_g2_x_2", U256::from(0)), - ("neg_s_g2_y_1", U256::from(0)), - ("neg_s_g2_y_2", U256::from(0)), - ("num_advices_user_challenges_offset", U256::from(0)), - ("last_quotient_x_cptr", U256::from(0)), - ("first_quotient_x_cptr", U256::from(0)), - ("instance_cptr", U256::from(0)), - ("challenges_offset", U256::from(0)), - ("gate_computations_len_offset", U256::from(0)), - ("permutation_computations_len_offset", U256::from(0)), - ("lookup_computations_len_offset", U256::from(0)), - ("pcs_computations_len_offset", U256::from(0)), - ("num_evals", U256::from(0)), - ("num_neg_lagranges", U256::from(0)), - ] - } else { - vec![ - ("vk_digest", U256::from(0)), - ("num_instances", U256::from(0)), - ("k", U256::from(0)), - ("n_inv", U256::from(0)), - ("omega", U256::from(0)), - ("omega_inv", U256::from(0)), - ("omega_inv_to_l", U256::from(0)), - ("has_accumulator", U256::from(0)), - ("acc_offset", U256::from(0)), - ("num_acc_limbs", U256::from(0)), - ("num_acc_limb_bits", U256::from(0)), - ("g1_x", U256::from(0)), - ("g1_y", U256::from(0)), - ("g2_x_1", U256::from(0)), - ("g2_x_2", U256::from(0)), - ("g2_y_1", U256::from(0)), - ("g2_y_2", U256::from(0)), - ("neg_s_g2_x_1", U256::from(0)), - ("neg_s_g2_x_2", U256::from(0)), - ("neg_s_g2_y_1", U256::from(0)), - ("neg_s_g2_y_2", U256::from(0)), - ] - } + fn dummy_vka_constants(&self) -> Vec<(&'static str, U256)> { + // Number of words the num_advices_user_challenges will take up. + let num_advices_len = self.meta.num_advices().len(); + let slots = 10; + let num_advices_user_challenges_capacity = + num_advices_len / slots + if num_advices_len % slots == 0 { 0 } else { 1 }; + + // assert that the predefined_labels length are less than the num_advices_user_challenges_capacity + assert!( + NUM_ADVICES_USER_CHALLENGES_LABELS.len() >= num_advices_user_challenges_capacity, + "predefined_labels length of 10 must be less than or equal to num_advices_user_challenges_capacity" + ); + + let mut constants = vec![ + ("vk_digest", U256::from(0)), + ("vk_mptr", U256::from(0)), + ("vk_len", U256::from(0)), + ("instance_cptr", U256::from(0)), + ("num_instances", U256::from(0)), + ("num_evals", U256::from(0)), + ("challenges_offset", U256::from(0)), + ("fsm_challenges", U256::from(0)), // The fsm position that we can move the num_advices_user_challenges to. + ("k", U256::from(0)), + ("n_inv", U256::from(0)), + ("omega", U256::from(0)), + ("omega_inv", U256::from(0)), + ("omega_inv_to_l", U256::from(0)), + ("has_accumulator", U256::from(0)), + ("acc_offset", U256::from(0)), + ("num_acc_limbs", U256::from(0)), + ("num_acc_limb_bits", U256::from(0)), + ("g1_x", U256::from(0)), + ("g1_y", U256::from(0)), + ("g2_x_1", U256::from(0)), + ("g2_x_2", U256::from(0)), + ("g2_y_1", U256::from(0)), + ("g2_y_2", U256::from(0)), + ("neg_s_g2_x_1", U256::from(0)), + ("neg_s_g2_x_2", U256::from(0)), + ("neg_s_g2_y_1", U256::from(0)), + ("neg_s_g2_y_2", U256::from(0)), + ("last_quotient_x_cptr", U256::from(0)), + ("first_quotient_x_cptr", U256::from(0)), + ("gate_computations_len_offset", U256::from(0)), + ("permutation_computations_len_offset", U256::from(0)), + ("lookup_computations_len_offset", U256::from(0)), + ("pcs_computations_len_offset", U256::from(0)), + ("num_neg_lagranges", U256::from(0)), + ]; + // Find the index of "fsm_challenges" and insert after it. + let fsm_challenges_index = constants + .iter() + .position(|(label, _)| *label == "fsm_challenges") + .unwrap(); + + // Create a vector of tuples with the num_advices_user_challenges elements. + let advices_entries: Vec<(&str, U256)> = (0..num_advices_user_challenges_capacity) + .map(|i| (NUM_ADVICES_USER_CHALLENGES_LABELS[i], U256::from(0))) + .collect(); + + // Insert the num_advices_user_challenges after the "fsm_challenges". + constants.splice( + fsm_challenges_index + 1..fsm_challenges_index + 1, + advices_entries, + ); + + constants + } + + fn dummy_vk_constants() -> Vec<(&'static str, U256)> { + vec![ + ("vk_digest", U256::from(0)), + ("num_instances", U256::from(0)), + ("k", U256::from(0)), + ("n_inv", U256::from(0)), + ("omega", U256::from(0)), + ("omega_inv", U256::from(0)), + ("omega_inv_to_l", U256::from(0)), + ("has_accumulator", U256::from(0)), + ("acc_offset", U256::from(0)), + ("num_acc_limbs", U256::from(0)), + ("num_acc_limb_bits", U256::from(0)), + ("g1_x", U256::from(0)), + ("g1_y", U256::from(0)), + ("g2_x_1", U256::from(0)), + ("g2_x_2", U256::from(0)), + ("g2_y_1", U256::from(0)), + ("g2_y_2", U256::from(0)), + ("neg_s_g2_x_1", U256::from(0)), + ("neg_s_g2_x_2", U256::from(0)), + ("neg_s_g2_y_1", U256::from(0)), + ("neg_s_g2_y_2", U256::from(0)), + ] } fn generate_vk(&self, reusable: bool) -> Halo2VerifyingKey { // Get the dummy constants using the new function - let mut constants = Self::dummy_vk_constants(reusable); + let mut constants = if reusable { + self.dummy_vka_constants() + } else { + Self::dummy_vk_constants() + }; // Fill in the actual values where applicable let domain = self.vk.get_domain(); @@ -387,6 +435,9 @@ impl<'a> SolidityGenerator<'a> { .copied() .collect::>(); + let mut fsm_challenges = 0x20 * (1 + self.num_instances); // initialize fsm_challenges with the space that loading the instances into memory will take up. + let mut max_advices_value = 0; // Initialize variable to track the maximum value of *num_advices * 0x40 + let num_advices_user_challenges: Vec = { let mut packed_words: Vec = vec![U256::from(0)]; let mut bit_counter = 8; @@ -401,17 +452,25 @@ impl<'a> SolidityGenerator<'a> { packed_words.push(U256::from(0)); bit_counter = 0; } - // Ensure that the packed num_advices and num_user_challenges data doesn't - // overflow. + + let advices_value = *num_advices * 0x40; + + // Ensure that the packed num_advices and num_user_challenges data doesn't overflow. assert!( - (*num_advices * 0x40) < 0x10000, + advices_value < 0x10000, "num_advices * 0x40 must be less than 0x10000" ); assert!( *num_user_challenges < 0x100, "num_user_challenges must be less than 0x100" ); - packed_words[last_idx] |= U256::from(*num_advices * 0x40) << bit_counter; + + // Track the maximum value of *num_advices * 0x40 + if advices_value > max_advices_value { + max_advices_value = advices_value; + } + + packed_words[last_idx] |= U256::from(advices_value) << bit_counter; bit_counter += 16; packed_words[last_idx] |= U256::from(*num_user_challenges) << bit_counter; bit_counter += 8; @@ -422,14 +481,25 @@ impl<'a> SolidityGenerator<'a> { packed_words }; + fsm_challenges += max_advices_value; + + // Iterate through the `num_advices_user_challenges` and update corresponding values in `constants` + for (i, value) in num_advices_user_challenges.iter().enumerate() { + if i >= NUM_ADVICES_USER_CHALLENGES_LABELS.len() { + panic!("word capacity for num_advices_user_challenges encoded vka data must be less than or equal to 10") + } + set_constant_value( + &mut dummy_vk.constants, + NUM_ADVICES_USER_CHALLENGES_LABELS[i], + *value, + ); + } + // Update constants let first_quotient_x_cptr = dummy_data.quotient_comm_cptr; let last_quotient_x_cptr = first_quotient_x_cptr + 2 * (self.meta.num_quotients - 1); let instance_cptr = U256::from(self.meta.proof_len(self.scheme) + 0xa4); - let num_advices_user_challenges_offset = - dummy_vk.len(true) + (const_expressions.len() * 0x20); - let gate_computations_len_offset = - num_advices_user_challenges_offset + (num_advices_user_challenges.len() * 0x20); + let gate_computations_len_offset = dummy_vk.len(true) + (const_expressions.len() * 0x20); let permutations_computations_len_offset = gate_computations_len_offset + (0x20 * gate_computations_dummy.len()); let lookup_computations_len_offset = @@ -437,6 +507,12 @@ impl<'a> SolidityGenerator<'a> { let pcs_computations_len_offset = lookup_computations_len_offset + (0x20 * lookup_computations_dummy.len()); + set_constant_value( + &mut dummy_vk.constants, + "fsm_challenges", + U256::from(fsm_challenges), + ); + set_constant_value(&mut dummy_vk.constants, "instance_cptr", instance_cptr); set_constant_value( &mut dummy_vk.constants, @@ -448,11 +524,6 @@ impl<'a> SolidityGenerator<'a> { "last_quotient_x_cptr", U256::from(last_quotient_x_cptr.value().as_usize()), ); - set_constant_value( - &mut dummy_vk.constants, - "num_advices_user_challenges_offset", - U256::from(num_advices_user_challenges_offset), - ); set_constant_value( &mut dummy_vk.constants, "gate_computations_len_offset", @@ -480,7 +551,6 @@ impl<'a> SolidityGenerator<'a> { fixed_comms: dummy_vk.fixed_comms, permutation_comms: dummy_vk.permutation_comms, const_expressions, - num_advices_user_challenges, gate_computations: gate_computations_dummy, permutation_computations: permutation_computations_dummy, lookup_computations: lookup_computations_dummy, @@ -593,7 +663,8 @@ impl<'a> SolidityGenerator<'a> { } fn generate_separate_verifier(&self) -> Halo2VerifierReusable { - let vk_const_offsets: HashMap<&'static str, U256> = Self::dummy_vk_constants(true) + let vk_const_offsets: HashMap<&'static str, U256> = self + .dummy_vka_constants() .iter() .enumerate() .map(|(idx, &(key, _))| (key, U256::from(idx * 32))) diff --git a/src/codegen/template.rs b/src/codegen/template.rs index 79983ed..8a536fd 100644 --- a/src/codegen/template.rs +++ b/src/codegen/template.rs @@ -35,7 +35,6 @@ impl Halo2VerifyingKey { #[template(path = "Halo2VerifyingArtifact.sol")] pub(crate) struct Halo2VerifyingArtifact { pub(crate) constants: Vec<(&'static str, U256)>, - pub(crate) num_advices_user_challenges: Vec, pub(crate) fixed_comms: Vec<(U256, U256)>, pub(crate) permutation_comms: Vec<(U256, U256)>, pub(crate) const_expressions: Vec, @@ -50,7 +49,6 @@ impl Halo2VerifyingArtifact { let len = self.constants.len() + (self.fixed_comms.len() + self.permutation_comms.len()) * 2 + self.const_expressions.len() - + self.num_advices_user_challenges.len() + self.gate_computations.len() + self.permutation_computations.len() + self.lookup_computations.len() diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index b391d2a..06f5824 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -48,9 +48,9 @@ contract Halo2VerifierReusable { } // Squeeze challenge without absorbing new input from calldata, - // by putting an extra 0x01 in memory[0x20] and squeeze by keccak256(memory[0..21]), + // by putting an extra 0x01 in memory[0x240] and squeeze by keccak256(memory[0..21]), // and store hash mod r as challenge in challenge_mptr, - // and push back hash in 0x00 as the first input for next squeeze. + // and push back hash in 0x220 as the first input for next squeeze. // Return updated (challenge_mptr). function squeeze_challenge_cont(challenge_mptr) -> ret { mstore8(0x20, 0x01) @@ -155,6 +155,21 @@ contract Halo2VerifierReusable { ret := and(ret, mload(0x00)) } + function reposition_challenge_len_data() -> ret0, ret1, ret2 { + let fsm_challenges := mload({{ vk_const_offsets["fsm_challenges"]|hex() }}) + let challenge_len_ptr := {{ vk_const_offsets["num_advices_user_challenges_0"]|hex() }} + let challenge_len_data := mload(challenge_len_ptr) + let num_words := and(challenge_len_data, BYTE_FLAG_BITMASK) + ret0 := num_words + num_words := mul(0x20, num_words) + challenge_len_data := shr(8, challenge_len_data) + for { let i := 0x20 } lt(i, num_words) { i := add(i, 0x20) } { + mstore(add(fsm_challenges, i), mload(add(challenge_len_ptr, i))) + } + ret1 := fsm_challenges + ret2 := challenge_len_data + } + // Returns start of computaions ptr and length of SoA layout memory // encoding for quotient evaluation data (gate, permutation and lookup computations) function soa_layout_metadata(offset, vk_mptr) -> ret0, ret1 { @@ -316,7 +331,7 @@ contract Halo2VerifierReusable { ret0, ret1, ret2 := expression_evals_packed(fsmp, code_ptr, expressions_word) } - function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { + function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { // iterate through the input_tables_len let evals := mload(evals_ptr) // We store a boolean flag in the first LSG byte of the evals ptr to determine if we need to load in a new table or reuse the previous table. @@ -704,33 +719,36 @@ contract Halo2VerifierReusable { // Initialize theta_mptr as 0x0 on the stack let theta_mptr := 0x0 { - // Load in the vk_digest, vk_mptr and vk_len - extcodecopy(vk, 0x0, 0x00, 0x60) + // Load in the vk_digest, vk_mptr and vk_len and the rest of the constants needed for the + // challenge data generation process. + extcodecopy(vk, 0x0, 0x00, 0x240) // Set the vk_mptr vk_mptr := mload(0x20) let vk_len := mload(0x40) - // Copy full vk into memory - extcodecopy(vk, vk_mptr, 0x00, vk_len) - let instance_cptr := mload(add(vk_mptr, {{ vk_const_offsets["instance_cptr"]|hex() }})) + let instance_cptr := mload({{ vk_const_offsets["instance_cptr"]|hex() }}) // Check valid length of proof success := and(success, eq(sub(instance_cptr, 0xa4), calldataload(PROOF_LEN_CPTR))) // Check valid length of instances - let num_instances := mload(add(vk_mptr,{{ vk_const_offsets["num_instances"]|hex() }})) + let num_instances := mload({{ vk_const_offsets["num_instances"]|hex() }}) success := and(success, eq(num_instances, calldataload(sub(instance_cptr,0x20)))) + + // Read instances and witness commitments and generate challenges + let hash_mptr := 0x20 + let proof_cptr := PROOF_CPTR - let num_evals := mul(0x20, mload(add(vk_mptr, {{ vk_const_offsets["num_evals"]|hex() }}))) - let challenge_mptr := add(vk_mptr, vk_len) // challenge_mptr is at the end of vk in memory // Set the theta_mptr (vk_mptr + vk_len + challenges_length) - theta_mptr := add(challenge_mptr, mload(add(vk_mptr, {{ vk_const_offsets["challenges_offset"]|hex() }}))) - let challenge_len_ptr := add(vk_mptr, mload(add(vk_mptr, {{ vk_const_offsets["num_advices_user_challenges_offset"]|hex() }}))) + theta_mptr := add(challenge_mptr, mload({{ vk_const_offsets["challenges_offset"]|hex() }})) + + // Move the challenge length data to the free static memory location for the challenge data generation process. + let num_words, challenge_len_ptr, challenge_len_data := reposition_challenge_len_data() + + let num_evals := mul(0x20, mload({{ vk_const_offsets["num_evals"]|hex() }})) - // Read instances and witness commitments and generate challenges - let hash_mptr := 0x20 for { let instance_cptr_end := add(instance_cptr, mul(0x20, num_instances)) } lt(instance_cptr, instance_cptr_end) @@ -743,10 +761,8 @@ contract Halo2VerifierReusable { hash_mptr := add(hash_mptr, 0x20) } - let challenge_len_data := mload(challenge_len_ptr) - let num_words := and(challenge_len_data, BYTE_FLAG_BITMASK) - challenge_len_data := shr(8, challenge_len_data) for { let i := 0 } lt(i, num_words) { i := add(i, 1) } { + challenge_len_ptr := add(challenge_len_ptr, 0x20) for { } challenge_len_data { } { // add proof_cpt to num advices len let proof_cptr_end := add(proof_cptr, and(challenge_len_data, PTR_BITMASK)) @@ -765,7 +781,6 @@ contract Halo2VerifierReusable { challenge_mptr := squeeze_challenge_cont(challenge_mptr) } } - challenge_len_ptr := add(challenge_len_ptr, 0x20) challenge_len_data := mload(challenge_len_ptr) } diff --git a/templates/Halo2VerifyingArtifact.sol b/templates/Halo2VerifyingArtifact.sol index 720490d..769e2c2 100644 --- a/templates/Halo2VerifyingArtifact.sol +++ b/templates/Halo2VerifyingArtifact.sol @@ -22,11 +22,7 @@ contract Halo2VerifyingArtifact { {%- for const in const_expressions %} mstore({{ (32 * (offset_2 + loop.index0))|hex_padded(4) }}, {{ const|hex_padded(64) }}) // const_expressions[{{ loop.index0 }}] {%- endfor %} - {%- let offset_3 = offset_2 + const_expressions.len() %} - {%- for word in num_advices_user_challenges %} - mstore({{ (32 * (offset_3 + loop.index0))|hex_padded(4) }}, {{ word|hex_padded(64) }}) // num_advices_challenges[{{ loop.index0 }}] - {%- endfor %} - {%- let offset_4 = offset_3 + num_advices_user_challenges.len() %} + {%- let offset_4 = offset_2 + const_expressions.len() %} mstore({{ (32 * offset_4)|hex_padded(4) }}, {{ (32 * gate_computations.length)|hex_padded(64) }}) // gate_computations length {%- let offset_5 = offset_4 + 1 %} {%- for packed_expression_word in gate_computations.packed_expression_words %} From d9d4b080c9f3349370aa5225e452f6517492dea5 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 9 Sep 2024 16:57:57 -0500 Subject: [PATCH 63/65] *fix logic bug in MV lookup and typo in last linear combine accum. --- templates/Halo2VerifierReusable.sol | 12 +++++++----- templates/Halo2VerifyingArtifact.sol | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 06f5824..7e38395 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -376,14 +376,16 @@ contract Halo2VerifierReusable { let last_idx := sub(outer_inputs_len, 0x20) for { let i := 0 } lt(i, outer_inputs_len) { i := add(i, 0x20) } { let tmp := mload(0xa0) + let j := 0x20 if eq(i, 0){ tmp := mload(0xc0) + j := 0x40 } - for { let j := 0 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { + for { } lt(j, outer_inputs_len) { j := add(j, 0x20) } { if eq(i, j) { continue } - tmp := mulmod(tmp, mload(j), R) + tmp := mulmod(tmp, mload(add(j, 0xa0)), R) } rhs := addmod(rhs, tmp, R) @@ -394,7 +396,7 @@ contract Halo2VerifierReusable { } let tmp := mload(0xa0) for { let j := 0x20 } lt(j, outer_inputs_len) { j := add(j, 0x20) } { - tmp := mulmod(tmp, mload(j), R) + tmp := mulmod(tmp, mload(add(j, 0xa0)), R) } rhs := addmod( rhs, @@ -944,7 +946,7 @@ contract Halo2VerifierReusable { } - // Compute quotient evavluation + // Compute quotient evaluation { let quotient_eval_numer let y := mload(add(theta_mptr, 0x60)) @@ -1346,7 +1348,7 @@ contract Halo2VerifierReusable { } // Random linear combine with accumulator - if mload(add(vk_mptr, {{ vk_const_offsets["first_quotient_x_cptr"]|hex() }})) { + if mload(add(vk_mptr, {{ vk_const_offsets["has_accumulator"]|hex() }})) { mstore(0x00, mload(add(theta_mptr, 0x100))) mstore(0x20, mload(add(theta_mptr, 0x120))) mstore(0x40, mload(add(theta_mptr, 0x140))) diff --git a/templates/Halo2VerifyingArtifact.sol b/templates/Halo2VerifyingArtifact.sol index 769e2c2..f8c81f1 100644 --- a/templates/Halo2VerifyingArtifact.sol +++ b/templates/Halo2VerifyingArtifact.sol @@ -45,7 +45,7 @@ contract Halo2VerifyingArtifact { mstore({{ (32 * (offset + 1 + loop.index0))|hex_padded(4) }}, {{ table_line|hex_padded(64) }}) // lookup_table_line [{{ loop.index0 }}] {%- endfor %} {%- for input in lookup.inputs %} - {%- let offset = offset + 1 + lookup.table_lines.len() + loop.index0 + input.acc %} + {%- let offset = offset + 1 + lookup.table_lines.len() + input.acc %} {%- for expression in input.expression %} mstore({{ (32 * (offset + loop.index0))|hex_padded(4) }}, {{ expression|hex_padded(64) }}) // input_expression [{{ loop.index0 }}] {%- endfor %} From 853b960901bd32e0e70fd471ad316d87c211ca61 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 16 Sep 2024 16:37:26 -0500 Subject: [PATCH 64/65] *non mv lookups --- .github/workflows/ci.yml | 4 +- src/codegen/evaluator.rs | 181 +++++++++++++++++++++++----- templates/Halo2VerifierReusable.sol | 134 ++++++++++++++++++-- 3 files changed, 278 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0439ff0..a2fe24c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,8 +26,10 @@ jobs: - name: Install solc run: (hash svm 2>/dev/null || cargo install svm-rs) && svm install 0.8.20 && svm use 0.8.20 && solc --version - - name: Run test + - name: Run test (mv lookups) run: cargo test --workspace --all-features --all-targets -- --nocapture + - name: Run test (non-mv lookups) + run: cargo test --workspace --all-targets -- --nocapture - name: Run separate example run: cargo run --package halo2_solidity_verifier --example separately --all-features -- --nocapture diff --git a/src/codegen/evaluator.rs b/src/codegen/evaluator.rs index 7115d95..1c3582c 100644 --- a/src/codegen/evaluator.rs +++ b/src/codegen/evaluator.rs @@ -507,7 +507,8 @@ pub struct InputsEncoded { } // Holds the encoded data stored in the VK artifact -// needed to perform the lookup computations for one lookup table +// needed to perform the lookup computations for one lookup table. +// In the case where non mv-lookups are used the inputs.len() == 1 #[derive(Clone, PartialEq, Eq)] pub struct LookupEncoded { pub(crate) evals: U256, @@ -532,7 +533,7 @@ pub struct LookupEncoded { // sum of the lengths of the inputs. impl LookupEncoded { pub fn len(&self) -> usize { - let base_len = 1; // Add 2 if table_inputs is none + let base_len = 1; base_len + self .inputs @@ -570,7 +571,6 @@ impl LookupsDataEncoded { } } } - #[derive(Debug)] pub(crate) struct EvaluatorDynamic<'a, F: PrimeField> { cs: &'a ConstraintSystem, @@ -696,22 +696,19 @@ where #[cfg(not(feature = "mv-lookup"))] pub fn quotient_eval_fsm_usage(&self) -> usize { - unimplemented!( - "quotient_eval_fsm_usage function is not implemented for the non mv-lookup version of the verifier" - ); - // let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); - - // let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; - - // // TODO implement the non mv lookup version of this calculation. - // let input_expressions_fsm_usage = 0; - - // itertools::max([ - // gate_computation_fsm_usage, - // permutation_computation_fsm_usage, - // input_expressions_fsm_usage, - // ]) - // .unwrap() + let gate_computation_fsm_usage = self.gate_computation_fsm_usage(); + + // 0x40 offset b/c that is where the fsm pointer starts in the permutations computation code block + let permutation_computation_fsm_usage = (self.data.permutation_z_evals.len() * 0x20) + 0x40; + + let input_expressions_fsm_usage = 0xc0; // offset to store theta offset ptrs used in the non mv lookup computations. + + itertools::max([ + gate_computation_fsm_usage, + permutation_computation_fsm_usage, + input_expressions_fsm_usage, + ]) + .unwrap() } #[cfg(feature = "mv-lookup")] @@ -760,6 +757,123 @@ where .unwrap() } + #[cfg(not(feature = "mv-lookup"))] + pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { + let input_tables = self + .cs + .lookups() + .iter() + .map(|lookup| { + let [packed_table_expr, packed_input_expr] = + [lookup.table_expressions(), lookup.input_expressions()].map(|expressions| { + let fsm = 0xc0; // offset to store theta offset ptrs used in the non mv lookup computations. + self.set_static_mem_ptr(fsm); + let (lines, inputs) = expressions + .iter() + .map(|expression| self.evaluate_encode(expression)) + .fold((Vec::new(), Vec::new()), |mut acc, result| { + acc.0.extend(result.0); + acc.1.push(result.1); + acc + }); + self.reset(); + self.encode_pack_expr_operations(lines, 8, Some(inputs)) + }); + (packed_table_expr, packed_input_expr) + }) + .collect_vec(); + + let is_contiguous = |positions: &[usize]| -> bool { + if positions.is_empty() { + return true; + } + for window in positions.windows(2) { + if window[1] != window[0] + 1 { + return false; + } + } + true + }; + + let mut accumulator = 0; + + let mut previous_table_lines: Option>> = None; + // Hshmap used to check for contigious table_expressions in the lookups + let mut table_expression_positions: HashMap>, Vec> = HashMap::new(); + + let lookups: Vec = izip!(input_tables, &self.data.lookup_evals) + .enumerate() + .map(|(lookup_index, (input_table, evals))| { + let (table_lines, inputs) = input_table; + let evals = self.encode_quintuple_evaluation_word(evals, 8); + let mut inner_accumulator = 0; + let inputs: Vec = inputs + .iter() + .map(|input_lines| { + let res = InputsEncoded { + expression: vec![input_lines.clone()], + acc: inner_accumulator, + }; + inner_accumulator += 1; + res + }) + .collect_vec(); + table_expression_positions + .entry(table_lines.clone()) + .or_default() + .push(lookup_index); + + let mut lookup_encoded = LookupEncoded { + evals, + table_lines: table_lines.clone(), + inputs: inputs.clone(), + acc: accumulator, + }; + // The first byte in the eval word will store whether we use the previous table lines or not. + // If we use the previous table lines then the byte will be 0x0 otherwise it will be 0x1. + if let Some(prev_lines) = &previous_table_lines { + if *prev_lines != table_lines { + lookup_encoded.evals |= U256::from(0x1); + } else { + lookup_encoded.table_lines = Vec::new(); + } + } else { + lookup_encoded.evals |= U256::from(0x1); + } + accumulator += lookup_encoded.len(); + + previous_table_lines = Some(table_lines); + lookup_encoded + }) + .collect_vec(); + // Check if any table_expressions (Vec>) have non-contiguous positions + for (table_exprs, positions) in table_expression_positions.iter() { + if !is_contiguous(positions) { + eprintln!( + "Warning: The table expressions `{:?}` are not contiguous across lookups. \ + Consider reordering your lookups so that all occurrences of this table expression \ + are adjacent to each other. This will result in more gas efficient verifications", + table_exprs + ); + } + } + + if lookups.is_empty() { + return LookupsDataEncoded::default(); + } + + let mut data = LookupsDataEncoded { + lookups, + meta_data: U256::from(0), + }; + // Insert the num_words and end_ptr to the beginning of the meta data word. + let end_ptr = (data.len() * 32) + offset; + data.meta_data = U256::from(end_ptr); + // Encode 0x1 into the next byte to indicate that is non-mv-lookup data. + data.meta_data |= U256::from(0x1) << 16; + data + } + #[cfg(feature = "mv-lookup")] pub fn lookup_computations(&self, offset: usize) -> LookupsDataEncoded { let evaluate_table = |expressions: &Vec<_>| { @@ -911,15 +1025,6 @@ where data } - #[cfg(not(feature = "mv-lookup"))] - pub fn lookup_computations(&self, _offset: usize) -> LookupsDataEncoded { - unimplemented!( - "Lookup_computations function is not implemented for the non mv-lookup version of the verifier" - ); - // // TODO implement non mv lookup version of this - // LookupsDataEncoded::default() - } - fn eval_encoded( &self, column_type: impl Into, @@ -971,9 +1076,23 @@ where bit_offset: usize, ) -> U256 { let (z_i, z_j, z_i_last) = value; - U256::from(z_i.ptr().value().as_usize() << bit_offset) - | U256::from(z_j.ptr().value().as_usize() << (bit_offset + 16)) - | U256::from(z_i_last.ptr().value().as_usize() << (bit_offset + 32)) + U256::from(z_i.ptr().value().as_usize()) << bit_offset + | U256::from(z_j.ptr().value().as_usize()) << (bit_offset + 16) + | U256::from(z_i_last.ptr().value().as_usize()) << (bit_offset + 32) + } + + #[allow(dead_code)] + fn encode_quintuple_evaluation_word( + &self, + value: &(Word, Word, Word, Word, Word), + bit_offset: usize, + ) -> U256 { + let (z_i, z_j, z_k, z_l, z_m) = value; + U256::from(z_i.ptr().value().as_usize()) << bit_offset + | U256::from(z_j.ptr().value().as_usize()) << (bit_offset + 16) + | U256::from(z_k.ptr().value().as_usize()) << (bit_offset + 32) + | U256::from(z_l.ptr().value().as_usize()) << (bit_offset + 48) + | U256::from(z_m.ptr().value().as_usize()) << (bit_offset + 64) } fn encode_pack_expr_operations( diff --git a/templates/Halo2VerifierReusable.sol b/templates/Halo2VerifierReusable.sol index 7e38395..b2fe468 100644 --- a/templates/Halo2VerifierReusable.sol +++ b/templates/Halo2VerifierReusable.sol @@ -247,7 +247,6 @@ contract Halo2VerifierReusable { let a := mload(and(expressions_word, PTR_BITMASK)) expressions_word := shr(16, expressions_word) let theta := mload(0x60) - let beta := mload(0x80) for { let j } lt(j, num_words_vars) { j := add(j, 0x20) } { for { } expressions_word { } { a := addmod( @@ -260,7 +259,6 @@ contract Halo2VerifierReusable { ret0 := add(code_ptr, add(i, j)) expressions_word := mload(ret0) } - a := addmod(a, beta, R) ret1 := expressions_word ret2 := a } @@ -326,9 +324,13 @@ contract Halo2VerifierReusable { ret2 := sub(acc, 0x20) } - function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word) -> ret0, ret1, ret2 { + function lookup_expr_evals_packed(fsmp, code_ptr, expressions_word, mv) -> ret0, ret1, ret2 { // expression evaluation. ret0, ret1, ret2 := expression_evals_packed(fsmp, code_ptr, expressions_word) + if mv { + // add the beta accum addmod if mv lookup + ret2 := addmod(ret2, mload(0x80), R) + } } function mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { @@ -353,7 +355,7 @@ contract Halo2VerifierReusable { // Due to the fact that lookups can share the previous table, we can cache it for reuse. let input_expression := mload(evals_ptr) if new_table { - evals_ptr, input_expression, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr)) + evals_ptr, input_expression, table := lookup_expr_evals_packed(0xa0, evals_ptr, mload(evals_ptr), 0x1) } // outer inputs len, stored in the first input expression word let outer_inputs_len := and(input_expression, PTR_BITMASK) @@ -362,7 +364,7 @@ contract Halo2VerifierReusable { for { let j := 0xa0 } lt(j, add(outer_inputs_len, 0xa0)) { j := add(j, 0x20) } { // call the expression_evals function to evaluate the input_lines let ident - evals_ptr, input_expression, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression) + evals_ptr, input_expression, ident := lookup_expr_evals_packed(j, evals_ptr, input_expression, 0x1) // store ident in free static memory mstore(j, ident) } @@ -426,6 +428,109 @@ contract Halo2VerifierReusable { ret2 := quotient_eval_numer } + function lookup_evals(table, evals_ptr, quotient_eval_numer, y) -> ret0, ret1, ret2 { + // iterate through the input_tables_len + let evals := mload(evals_ptr) + // We store a boolean flag in the first LSG byte of the evals ptr to determine if we need to load in a new table or reuse the previous table. + let new_table := and(evals, BYTE_FLAG_BITMASK) + evals := shr(8, evals) + let z := and(evals, PTR_BITMASK) + evals := shr(16, evals) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + addmod( + mload(0x20), + mulmod( + mload(0x20), + sub(R, calldataload(z)), + R + ), + R + ), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + mload(0x00), + addmod( + mulmod(calldataload(z), calldataload(z), R), + sub(R, calldataload(z)), + R + ), + R + ), + R + ) + // load in the lookup_table_lines from the evals_ptr + evals_ptr := add(evals_ptr, 0x20) + // Due to the fact that lookups can share the previous table, we can cache it for reuse. + let input_expression := mload(evals_ptr) + if new_table { + evals_ptr, input_expression, table := lookup_expr_evals_packed(0xc0, evals_ptr, mload(evals_ptr), 0x0) + } + // call the expression_evals function to evaluate the input_lines + let input + evals_ptr, input_expression, input := lookup_expr_evals_packed(0xc0, evals_ptr, input_expression, 0x0) + let p_input := and(shr(16, evals), PTR_BITMASK) + let p_table := and(shr(48, evals), PTR_BITMASK) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + addmod( + 1, + sub(R, addmod(mload(0x40), mload(0x0), R)), + R + ), + addmod( + mulmod( + calldataload(and(evals, PTR_BITMASK)), + mulmod( + addmod(calldataload(p_input), mload(0x80), R), + addmod(calldataload(p_table), mload(0xa0), R), + R + ), + R + ), + sub( + R, + mulmod( + calldataload(z), + mulmod(addmod(input, mload(0x80), R), addmod(table, mload(0xa0), R), R), + R + ) + ), + R + ), + R + ), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod(mload(0x20), addmod(calldataload(p_input), sub(R, calldataload(p_table)), R), R), + R + ) + quotient_eval_numer := addmod( + mulmod(quotient_eval_numer, y, R), + mulmod( + addmod( + 1, + sub(R, addmod(mload(0x40), mload(0x0), R)), R), + mulmod( + addmod(calldataload(p_input), sub(R, calldataload(p_table)), R), + addmod(calldataload(p_input), sub(R, calldataload(and(shr(32, evals), PTR_BITMASK))), R), + R + ), + R + ), + R + ) + ret0 := evals_ptr + ret1 := table + ret2 := quotient_eval_numer + } + function point_rots(pcs_computations, pcs_ptr, word_shift, x_pow_of_omega, omega) -> ret0, ret1 { // Extract the 32 LSG bits (4 bytes) from the pcs_computations word to get the max rot let values_max_rot := and(pcs_computations, BYTE_FLAG_BITMASK) @@ -1032,15 +1137,26 @@ contract Halo2VerifierReusable { mstore(0x40, mload(add(theta_mptr, 0x1E0))) // l_blind mstore(0x60, mload(theta_mptr)) // theta mstore(0x80, mload(add(theta_mptr, 0x20))) // beta - let evals_ptr, end_ptr := soa_layout_metadata({{ + let evals_ptr, meta_data := soa_layout_metadata({{ vk_const_offsets["lookup_computations_len_offset"]|hex() }}, vk_mptr) // lookup meta data contains 32 byte flags for indicating if we need to do a lookup table lines // expression evaluation or we can use the previous one cached in the table var. - if end_ptr { + if meta_data { let table - for { } lt(evals_ptr, end_ptr) { } { - evals_ptr, table, quotient_eval_numer := mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) + let end_ptr := and(meta_data, PTR_BITMASK) + let mv := and(shr(16, meta_data), BYTE_FLAG_BITMASK) + switch mv + case 0x0 { + for { } lt(evals_ptr, end_ptr) { } { + evals_ptr, table, quotient_eval_numer := mv_lookup_evals(table, evals_ptr, quotient_eval_numer, y) + } + } + case 0x1 { + mstore(0xA0, mload(add(theta_mptr, 0x40))) // gamma + for { } lt(evals_ptr, end_ptr) { } { + evals_ptr, table, quotient_eval_numer := lookup_evals(table, evals_ptr, quotient_eval_numer, y) + } } } } From fa1c577c135dc51ada2d048fabe7663908a7d953 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 4 Oct 2024 17:11:06 +0800 Subject: [PATCH 65/65] *bit flip fuzzing tests --- src/evm.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/test.rs | 28 +++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/evm.rs b/src/evm.rs index 6142c00..5d57097 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -51,7 +51,8 @@ pub(crate) mod test { pub use revm; use revm::{ primitives::{ - Address, CfgEnv, CfgEnvWithHandlerCfg, ExecutionResult, Output, TransactTo, TxEnv, + Address, Bytes, CfgEnv, CfgEnvWithHandlerCfg, ExecutionResult, HaltReason, Output, + TransactTo, TxEnv, }, Evm as EVM, InMemoryDB, }; @@ -73,6 +74,8 @@ pub(crate) mod test { .stderr(Stdio::piped()) .arg("--bin") .arg("--optimize") + .arg("--model-checker-targets") + .arg("underflow,overflow") .arg("-") .spawn() { @@ -81,7 +84,7 @@ pub(crate) mod test { panic!("Command 'solc' not found"); } Err(err) => { - panic!("Failed to spwan process with command 'solc':\n{err}"); + panic!("Failed to spawn process with command 'solc':\n{err}"); } }; process @@ -189,6 +192,20 @@ pub(crate) mod test { } } + /// Apply call transaction to given `address` with `calldata` with the expectation of failure. + /// Returns `gas_used` and `return_data`. + /// + /// # Panics + /// Panics if execution succeeds. + pub fn call_fail(&mut self, address: Address, calldata: Vec) { + let (_, _) = self.transact_failure_or_panic(TxEnv { + gas_limit: u64::MAX, + transact_to: TransactTo::Call(address), + data: calldata.into(), + ..Default::default() + }); + } + fn transact_success_or_panic(&mut self, tx: TxEnv) -> (u64, Output) { self.evm.context.evm.env.tx = tx; let result = self.evm.transact_commit().unwrap(); @@ -220,5 +237,41 @@ pub(crate) mod test { ), } } + + fn transact_failure_or_panic(&mut self, tx: TxEnv) -> (u64, Result) { + self.evm.context.evm.env.tx = tx; + let result = self.evm.transact_commit().unwrap(); + self.evm.context.evm.env.tx = Default::default(); + match result { + ExecutionResult::Success { + gas_used, + output, + logs, + .. + } => { + if !logs.is_empty() { + println!("--- logs from {} ---", logs[0].address); + for (log_idx, log) in logs.iter().enumerate() { + println!("log#{log_idx}"); + for (topic_idx, topic) in log.topics().iter().enumerate() { + println!(" topic{topic_idx}: {topic:?}"); + } + } + println!("--- end ---"); + } + panic!("Transaction succeeds unexpectedly with gas_used {gas_used} and output {output:?}") + } + ExecutionResult::Revert { gas_used, output } => { + println!("Transaction reverts with gas_used {gas_used} and output {output:#x}"); + (gas_used, Ok(output)) + } + ExecutionResult::Halt { reason, gas_used } => { + println!( + "Transaction halts unexpectedly with gas_used {gas_used} and reason {reason:?}" + ); + (gas_used, Err(reason)) + } + } + } } } diff --git a/src/test.rs b/src/test.rs index d1f18a7..9cfb26b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -5,7 +5,9 @@ use crate::{ FN_SIG_VERIFY_PROOF, FN_SIG_VERIFY_PROOF_WITH_VK_ADDRESS, }; use halo2_proofs::halo2curves::bn256::{Bn256, Fr}; +use rand::Rng; use rand::{rngs::StdRng, RngCore, SeedableRng}; +use revm::primitives::Address; use sha3::Digest; use std::{fs::File, io::Write}; @@ -53,7 +55,6 @@ fn run_render>() { let generator = SolidityGenerator::new(¶ms, &vk, Bdfg21, instances.len()) .set_acc_encoding(acc_encoding); let verifier_solidity = generator.render().unwrap(); - // println!("Verifier solidity conjoined: {verifier_solidity}"); let verifier_creation_code = compile_solidity(verifier_solidity); let verifier_creation_code_size = verifier_creation_code.len(); @@ -68,6 +69,9 @@ fn run_render>() { let (gas_cost, output) = evm.call(verifier_address, encode_calldata(None, &proof, &instances)); assert_eq!(output, [vec![0; 31], vec![1]].concat()); println!("Gas cost conjoined: {gas_cost}"); + + // Fuzzing tests + bit_flip_fuzzing_test::(verifier_address, proof, instances, &mut evm); } fn run_render_separately>() { @@ -118,6 +122,28 @@ fn run_render_separately>() { ); assert_eq!(output, [vec![0; 31], vec![1]].concat()); println!("Gas cost separate: {gas_cost}"); + bit_flip_fuzzing_test::(verifier_address, proof, instances, &mut evm); + } +} + +fn bit_flip_fuzzing_test>( + verifier_address: Address, + proof: Vec, + instances: Vec, + evm: &mut Evm, +) { + let mut rng = rand::thread_rng(); + for i in 0..10 { + let mut modified_proof = proof.clone(); + let random_byte = rng.gen_range(0..modified_proof.len()); + let random_bit = rng.gen_range(0..8); + modified_proof[random_byte] ^= 1 << random_bit; + + evm.call_fail( + verifier_address, + encode_calldata(None, &modified_proof, &instances), + ); + println!("Modified proof {} failed verification as expected", i); } }