From 9cf007d11e6e43b2799abb771e2ee8af7f75798f Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 13 Aug 2024 23:26:13 -0700 Subject: [PATCH] feat: add Groth16 Verify component (#42) * feat: add Groth16 Verify component * chore: update rust toolchain * chore: fix lint * chore: lint --- axiom-codec/src/constants.rs | 2 +- axiom-codec/src/decoder/native.rs | 21 +- axiom-codec/src/encoder/field_elements.rs | 25 +- axiom-codec/src/encoder/native.rs | 27 +- axiom-codec/src/lib.rs | 1 - axiom-codec/src/types/field_elements.rs | 3 +- axiom-codec/src/types/native.rs | 4 +- axiom-components/src/groth16/mod.rs | 419 ++++++++++++++---- axiom-components/src/groth16/native.rs | 95 ++++ axiom-components/src/groth16/test.rs | 383 ++++++++++++---- .../default_public_inputs_modified.json | 4 + axiom-components/src/groth16/types.rs | 179 ++++++++ axiom-components/src/groth16/utils.rs | 12 +- axiom-components/src/groth16/verifier/mod.rs | 97 ++++ .../src/groth16/verifier/native.rs | 35 ++ axiom-components/src/groth16/verifier/test.rs | 172 +++++++ .../groth16/verifier/test_data/default.json | 99 +++++ .../verifier/test_data/default_proof.json | 28 ++ .../test_data/default_public_inputs.json | 4 + .../src/groth16/verifier/test_data/proof.json | 28 ++ .../verifier/test_data/public_inputs.json | 13 + .../groth16/verifier/test_data/puzzle.json | 144 ++++++ .../src/groth16/verifier/types.rs | 122 +++++ axiom-components/src/lib.rs | 3 +- axiom-components/src/scaffold/mod.rs | 6 +- axiom-components/src/utils/testing.rs | 4 +- axiom-eth/Cargo.toml | 2 +- .../component/circuit/comp_circuit_impl.rs | 16 +- .../src/utils/component/promise_collector.rs | 28 +- axiom-query/Cargo.toml | 4 +- axiom-query/configs/test/results_root.json | 15 +- axiom-query/src/components/results/circuit.rs | 42 +- .../src/components/results/results_root.rs | 15 +- axiom-query/src/components/results/tests.rs | 33 +- axiom-query/src/keygen/agg/single_type.rs | 3 +- axiom-query/src/keygen/agg/subquery_agg.rs | 4 + axiom-query/src/keygen/shard/mod.rs | 12 +- axiom-query/src/lib.rs | 1 - .../src/subquery_aggregation/circuit.rs | 6 +- axiom-query/src/subquery_aggregation/tests.rs | 2 + axiom-query/src/subquery_aggregation/types.rs | 2 + axiom-query/src/utils/codec.rs | 6 +- axiom-query/src/verify_compute/circuit.rs | 4 + .../src/verify_compute/tests/aggregation.rs | 1 + axiom-query/src/verify_compute/tests/mod.rs | 10 +- axiom-query/src/verify_compute/tests/prove.rs | 7 +- axiom-query/src/verify_compute/types.rs | 8 +- rust-toolchain | 2 +- 48 files changed, 1908 insertions(+), 245 deletions(-) create mode 100644 axiom-components/src/groth16/native.rs create mode 100644 axiom-components/src/groth16/test_data/default_public_inputs_modified.json create mode 100644 axiom-components/src/groth16/types.rs create mode 100644 axiom-components/src/groth16/verifier/mod.rs create mode 100644 axiom-components/src/groth16/verifier/native.rs create mode 100644 axiom-components/src/groth16/verifier/test.rs create mode 100644 axiom-components/src/groth16/verifier/test_data/default.json create mode 100644 axiom-components/src/groth16/verifier/test_data/default_proof.json create mode 100644 axiom-components/src/groth16/verifier/test_data/default_public_inputs.json create mode 100644 axiom-components/src/groth16/verifier/test_data/proof.json create mode 100644 axiom-components/src/groth16/verifier/test_data/public_inputs.json create mode 100644 axiom-components/src/groth16/verifier/test_data/puzzle.json create mode 100644 axiom-components/src/groth16/verifier/types.rs diff --git a/axiom-codec/src/constants.rs b/axiom-codec/src/constants.rs index 21a58fc8..823bb0f1 100644 --- a/axiom-codec/src/constants.rs +++ b/axiom-codec/src/constants.rs @@ -5,7 +5,7 @@ pub const FIELD_IDX_BYTES: usize = 4; pub const MAX_SOLIDITY_MAPPING_KEYS: usize = 4; pub const MAX_SUBQUERY_INPUTS: usize = 13; pub const MAX_SUBQUERY_OUTPUTS: usize = 2; -pub const NUM_SUBQUERY_TYPES: usize = 8; +pub const NUM_SUBQUERY_TYPES: usize = 9; pub const SOURCE_CHAIN_ID_BYTES: usize = 8; pub const SUBQUERY_TYPE_BYTES: usize = 2; pub const USER_ADVICE_COLS: usize = 4; diff --git a/axiom-codec/src/decoder/native.rs b/axiom-codec/src/decoder/native.rs index bc978813..d36182ed 100644 --- a/axiom-codec/src/decoder/native.rs +++ b/axiom-codec/src/decoder/native.rs @@ -1,9 +1,13 @@ use std::io::{self, Read, Result}; -use axiom_components::ecdsa::ECDSAComponentNativeInput; -use axiom_eth::halo2curves::bn256::G1Affine; +use axiom_components::{ + ecdsa::ECDSAComponentNativeInput, + groth16::types::{Groth16NativeInput, Groth16VerifierComponentInput}, + utils::flatten::InputFlatten, +}; +use axiom_eth::halo2curves::bn256::{Fr, G1Affine}; use byteorder::{BigEndian, ReadBytesExt}; -use ethers_core::types::Bytes; +use ethers_core::types::{Bytes, H256}; use crate::{ constants::MAX_SOLIDITY_MAPPING_KEYS, @@ -31,6 +35,7 @@ impl TryFrom for AnySubquery { decode_solidity_nested_mapping_subquery(&mut reader)?, ), SubqueryType::ECDSA => AnySubquery::ECDSA(decode_ecdsa_subquery(&mut reader)?), + SubqueryType::Groth16 => AnySubquery::Groth16(decode_groth16_subquery(&mut reader)?), }) } } @@ -47,6 +52,7 @@ impl TryFrom for SubqueryType { 5 => Ok(Self::Receipt), 6 => Ok(Self::SolidityNestedMapping), 7 => Ok(Self::ECDSA), + 8 => Ok(Self::Groth16), // 7 => Ok(Self::BeaconValidator), _ => Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid SubqueryType")), } @@ -162,3 +168,12 @@ pub fn decode_ecdsa_subquery(mut reader: impl Read) -> Result Result { + let mut bytes: Vec = vec![]; + for _ in 0..Groth16VerifierComponentInput::::NUM_FE { + let fe = read_h256(&mut reader)?; + bytes.push(fe); + } + Ok(Groth16NativeInput { bytes }) +} diff --git a/axiom-codec/src/encoder/field_elements.rs b/axiom-codec/src/encoder/field_elements.rs index 6775f3fc..5bd22005 100644 --- a/axiom-codec/src/encoder/field_elements.rs +++ b/axiom-codec/src/encoder/field_elements.rs @@ -1,6 +1,9 @@ use std::io; -use axiom_components::{ecdsa::ECDSAComponentInput, utils::flatten::InputFlatten}; +use axiom_components::{ + ecdsa::ECDSAComponentInput, groth16::types::Groth16VerifierComponentInput, + utils::flatten::InputFlatten, +}; use axiom_eth::halo2curves::bn256::Fr; use ethers_core::types::{Bytes, H256}; @@ -30,7 +33,14 @@ pub const NUM_FE_ANY: [usize; NUM_SUBQUERY_TYPES] = [ NUM_FE_RECEIPT, NUM_FE_SOLIDITY_NESTED_MAPPING, ECDSAComponentInput::::NUM_FE, + Groth16VerifierComponentInput::::NUM_FE, ]; +/// The enabled types affects the subquery hash calculation +/// (the circuit iterates through the NUM_FE of each enabled type) and so to maintain +/// backwards compatibility, the default is to enable the original V2 mainnet launch +/// enabled types. +pub const DEFAULT_V2_ENABLED_TYPES: [bool; NUM_SUBQUERY_TYPES] = + [true, true, true, true, true, true, true, false, false]; /// The index of the mapping depth in [`FieldSolidityNestedMappingSubquery`]. pub const FIELD_SOLIDITY_NESTED_MAPPING_DEPTH_IDX: usize = 4; @@ -57,6 +67,8 @@ pub const BITS_PER_FE_SOLIDITY_NESTED_MAPPING: [usize; NUM_FE_SOLIDITY_NESTED_MA bits_per_fe_solidity_nested_mapping(); pub const BYTES_PER_FE_ECDSA: [usize; ECDSAComponentInput::::NUM_FE] = [16; ECDSAComponentInput::::NUM_FE]; +pub const BYTES_PER_FE_GROTH16: [usize; Groth16VerifierComponentInput::::NUM_FE] = + [32; Groth16VerifierComponentInput::::NUM_FE]; pub const BYTES_PER_FE_ANY: [&[usize]; NUM_SUBQUERY_TYPES] = [ &[], &BYTES_PER_FE_HEADER, @@ -66,6 +78,7 @@ pub const BYTES_PER_FE_ANY: [&[usize]; NUM_SUBQUERY_TYPES] = [ &BYTES_PER_FE_RECEIPT, &BYTES_PER_FE_SOLIDITY_NESTED_MAPPING, &BYTES_PER_FE_ECDSA, + &BYTES_PER_FE_GROTH16, ]; pub const BYTES_PER_FE_SUBQUERY_OUTPUT: usize = 16; @@ -160,6 +173,7 @@ impl From for FieldSubquery { FieldSolidityNestedMappingSubquery::from(subquery).into() } AnySubquery::ECDSA(subquery) => ECDSAComponentInput::from(subquery).into(), + AnySubquery::Groth16(subquery) => Groth16VerifierComponentInput::from(subquery).into(), } } } @@ -345,3 +359,12 @@ impl From> for FieldSubquery { Self { subquery_type: SubqueryType::ECDSA, encoded_subquery_data } } } + +impl From> for FieldSubquery { + fn from(value: Groth16VerifierComponentInput) -> Self { + let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS]; + encoded_subquery_data[..Groth16VerifierComponentInput::::NUM_FE] + .copy_from_slice(&value.flatten()); + Self { subquery_type: SubqueryType::Groth16, encoded_subquery_data } + } +} diff --git a/axiom-codec/src/encoder/native.rs b/axiom-codec/src/encoder/native.rs index 9f7ef524..6721930b 100644 --- a/axiom-codec/src/encoder/native.rs +++ b/axiom-codec/src/encoder/native.rs @@ -3,7 +3,7 @@ use std::{ iter, }; -use axiom_components::ecdsa::ECDSAComponentNativeInput; +use axiom_components::{ecdsa::ECDSAComponentNativeInput, groth16::types::Groth16NativeInput}; use byteorder::{BigEndian, WriteBytesExt}; use ethers_core::{types::H256, utils::keccak256}; @@ -219,6 +219,16 @@ pub fn encode_ecdsa_component_native_input( Ok(()) } +pub fn encode_groth16_native_input( + writer: &mut impl Write, + input: Groth16NativeInput, +) -> Result<()> { + for fe in input.bytes { + writer.write_all(&fe[..])?; + } + Ok(()) +} + impl From for Subquery { fn from(value: HeaderSubquery) -> Self { let mut bytes = vec![]; @@ -278,6 +288,14 @@ impl From for Subquery { } } +impl From for Subquery { + fn from(value: Groth16NativeInput) -> Self { + let mut bytes = vec![]; + encode_groth16_native_input(&mut bytes, value).unwrap(); + Self { subquery_type: SubqueryType::Groth16, encoded_subquery_data: bytes.into() } + } +} + impl From for Subquery { fn from(value: AnySubquery) -> Self { match value { @@ -291,6 +309,7 @@ impl From for Subquery { AnySubquery::Receipt(subquery) => subquery.into(), AnySubquery::SolidityNestedMapping(subquery) => subquery.into(), AnySubquery::ECDSA(subquery) => subquery.into(), + AnySubquery::Groth16(subquery) => subquery.into(), } } } @@ -331,3 +350,9 @@ impl From for AnySubquery { AnySubquery::ECDSA(value) } } + +impl From for AnySubquery { + fn from(value: Groth16NativeInput) -> Self { + AnySubquery::Groth16(value) + } +} diff --git a/axiom-codec/src/lib.rs b/axiom-codec/src/lib.rs index 5b91790c..40087f71 100644 --- a/axiom-codec/src/lib.rs +++ b/axiom-codec/src/lib.rs @@ -1,5 +1,4 @@ #![feature(trait_alias)] -#![feature(io_error_other)] pub use axiom_eth::utils::hilo::HiLo; pub use axiom_eth::Field; diff --git a/axiom-codec/src/types/field_elements.rs b/axiom-codec/src/types/field_elements.rs index d8bfba9c..7c207f49 100644 --- a/axiom-codec/src/types/field_elements.rs +++ b/axiom-codec/src/types/field_elements.rs @@ -1,6 +1,6 @@ use std::{fmt::Debug, ops::Deref}; -use axiom_components::ecdsa::ECDSAComponentInput; +use axiom_components::{ecdsa::ECDSAComponentInput, groth16::types::Groth16VerifierComponentInput}; use serde::{Deserialize, Serialize}; use crate::{ @@ -173,3 +173,4 @@ pub type FieldReceiptSubqueryResult = AnySubqueryResult = AnySubqueryResult, HiLo>; pub type FieldECDSASubqueryResult = AnySubqueryResult, T>; +pub type FieldGroth16SubqueryResult = AnySubqueryResult, T>; diff --git a/axiom-codec/src/types/native.rs b/axiom-codec/src/types/native.rs index 2a2c90a2..c7e7068f 100644 --- a/axiom-codec/src/types/native.rs +++ b/axiom-codec/src/types/native.rs @@ -1,4 +1,4 @@ -use axiom_components::ecdsa::ECDSAComponentNativeInput; +use axiom_components::{ecdsa::ECDSAComponentNativeInput, groth16::types::Groth16NativeInput}; use axiom_eth::halo2curves::bn256::G1Affine; use ethers_core::types::{Address, Bytes, H256, U256}; use serde::{Deserialize, Serialize}; @@ -78,6 +78,7 @@ pub enum SubqueryType { Receipt = 5, SolidityNestedMapping = 6, ECDSA = 7, + Groth16 = 8, } #[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -90,6 +91,7 @@ pub enum AnySubquery { Receipt(ReceiptSubquery), SolidityNestedMapping(SolidityNestedMappingSubquery), ECDSA(ECDSAComponentNativeInput), + Groth16(Groth16NativeInput), } #[derive(Clone, Debug, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/axiom-components/src/groth16/mod.rs b/axiom-components/src/groth16/mod.rs index ac81eeba..6a7708e9 100644 --- a/axiom-components/src/groth16/mod.rs +++ b/axiom-components/src/groth16/mod.rs @@ -1,51 +1,105 @@ +use std::cmp::min; + use axiom_eth::{ halo2_base::{ - gates::{flex_gate::threads::parallelize_core, RangeChip}, + gates::{flex_gate::threads::parallelize_core, RangeChip, RangeInstructions}, AssignedValue, Context, }, rlc::circuit::builder::RlcCircuitBuilder, - utils::build_utils::dummy::DummyFrom, + utils::{ + build_utils::dummy::DummyFrom, + bytes_be_to_uint, + component::{types::PoseidonHasher, utils::create_hasher}, + hilo::HiLo, + uint_to_bytes_be, uint_to_bytes_le, + }, Field, }; -use component_derive::{Component, ComponentIO, ComponentParams}; -use groth_verifier::{types::*, *}; +use component_derive::Component; use halo2_ecc::{ bn254::{pairing::PairingChip, Fp2Chip, FpChip}, ecc::EccChip, + halo2_base::{gates::GateInstructions, safe_types::SafeByte, utils::biguint_to_fe}, }; -use serde::{Deserialize, Serialize}; +use num_bigint::BigUint; +use serde_json; -use self::utils::*; +use self::{ + test::default_groth16_subquery_input, + types::{Groth16VerifierComponentInput, Groth16VerifierComponentParams, Groth16VerifierInput}, + utils::*, + verifier::{ + types::{ProofAssigned, VerifyingKeyAssigned}, + verify_proof, + }, +}; use crate::{ + groth16::types::{ + Groth16VerifierComponentOutput, Groth16VerifierComponentProof, + Groth16VerifierComponentVerificationKey, + }, scaffold::{BasicComponentScaffold, BasicComponentScaffoldIO}, - utils::flatten::{FixLenVec, VecKey}, + utils::flatten::InputFlatten, }; -#[cfg(test)] +pub mod native; pub mod test; +pub mod types; pub mod utils; +pub mod verifier; -#[derive(Default, Clone, ComponentParams)] -pub struct Groth16VerifierComponentParams { - pub capacity: usize, - pub limb_bits: usize, - pub num_limbs: usize, -} +pub const NUM_BYTES_PER_FE: usize = 31; +pub const NUM_FE_PER_CHUNK: usize = 13; +pub const NUM_BYTES_PROOF: usize = (4 + 8 + 4) * 16; +pub const MAX_NUM_BYTES_PER_CHUNK: usize = NUM_BYTES_PER_FE * NUM_FE_PER_CHUNK; +pub const NULL_CHUNK_VAL: usize = 2; +pub const NUM_FE_PROOF: usize = (NUM_BYTES_PROOF + NUM_BYTES_PER_FE - 1) / NUM_BYTES_PER_FE; -#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] -pub struct Groth16VerifierComponentVerificationKey { - pub alpha_g1: HiLoPoint, - pub beta_g2: HiLoPair, - pub gamma_g2: HiLoPair, - pub delta_g2: HiLoPair, - pub gamma_abc_g1: VecKey, MAX_PUBLIC_INPUTS>, // will create vector of size MAX_PUBLIC_INPUTS + 1 +#[derive(Debug, Clone, Copy)] +pub struct Groth16InputConstants { + pub max_pi: usize, + pub gamma_abc_g1_len: usize, + pub num_fe_hilo_vkey: usize, + pub num_bytes_vkey: usize, + pub num_bytes_per_input_without_pi: usize, + pub num_fe_vkey: usize, + pub num_fe_per_input_without_pi: usize, + pub num_fe_per_input: usize, + pub num_chunks: usize, + pub max_num_fe_per_input: usize, + pub max_num_fe_per_input_no_hash: usize, + pub max_num_bytes_per_input: usize, + pub num_fe_padding: usize, } -#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] -pub struct Groth16VerifierComponentProof { - pub a: HiLoPoint, - pub b: HiLoPair, - pub c: HiLoPoint, +pub fn get_groth16_consts_from_max_pi(max_pi: usize) -> Groth16InputConstants { + let gamma_abc_g1_len = max_pi + 1; + let num_fe_hilo_vkey = 4 + 8 + 8 + 8 + 4 * gamma_abc_g1_len; + let num_bytes_vkey = num_fe_hilo_vkey * 16 + 1; + let num_bytes_per_input_without_pi = num_bytes_vkey + NUM_BYTES_PROOF; + let num_fe_vkey = (num_bytes_vkey + NUM_BYTES_PER_FE - 1) / NUM_BYTES_PER_FE; + let num_fe_per_input_without_pi = num_fe_vkey + NUM_FE_PROOF; + let num_fe_per_input = num_fe_per_input_without_pi + max_pi; + let num_chunks = (num_fe_per_input + NUM_FE_PER_CHUNK - 1) / NUM_FE_PER_CHUNK; + let max_num_fe_per_input = NUM_FE_PER_CHUNK * num_chunks; + let max_num_fe_per_input_no_hash = max_num_fe_per_input - 1; + let max_num_bytes_per_input = NUM_BYTES_PER_FE * max_num_fe_per_input_no_hash; + let num_fe_padding = max_num_fe_per_input_no_hash - num_fe_per_input; + Groth16InputConstants { + max_pi, + gamma_abc_g1_len, + num_fe_hilo_vkey, + num_bytes_vkey, + num_bytes_per_input_without_pi, + num_fe_vkey, + num_fe_per_input_without_pi, + num_fe_per_input, + num_chunks, + max_num_fe_per_input, + max_num_fe_per_input_no_hash, + max_num_bytes_per_input, + num_fe_padding, + } } impl Groth16VerifierComponentProof> { @@ -63,9 +117,7 @@ impl Groth16VerifierComponentProof> { } } -impl - Groth16VerifierComponentVerificationKey, MAX_PUBLIC_INPUTS> -{ +impl Groth16VerifierComponentVerificationKey> { pub fn convert_to_affine( &self, ctx: &mut Context, @@ -95,74 +147,89 @@ impl } } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] -pub struct Groth16VerifierComponentInput { - pub vk: Groth16VerifierComponentVerificationKey, - pub proof: Groth16VerifierComponentProof, - pub public_inputs: FixLenVec, // MAX_PUBLIC_INPUTS - pub num_public_inputs: T, -} - -#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] -pub struct Groth16VerifierComponentOutput { - pub success: T, -} - #[derive(Component)] -pub struct Groth16VerifierComponent( - std::marker::PhantomData, -); +pub struct Groth16VerifierComponent(std::marker::PhantomData); -impl BasicComponentScaffold - for Groth16VerifierComponent -{ +impl BasicComponentScaffold for Groth16VerifierComponent { type Params = Groth16VerifierComponentParams; fn virtual_assign_phase0( params: Groth16VerifierComponentParams, builder: &mut RlcCircuitBuilder, - input: Vec>, + input: Vec>, ) -> BasicComponentScaffoldIO { let range = builder.base.range_chip(); + let input_constants = get_groth16_consts_from_max_pi(params.max_public_inputs); + assert!(input.len() % input_constants.num_chunks == 0); + let chunked_input = input.chunks(input_constants.num_chunks).collect::>(); + let zero = builder.base.main(0).load_constant(F::ZERO); + let two = builder + .base + .main(0) + .load_constant(F::from(NULL_CHUNK_VAL as u64)); + + let null_output = Groth16VerifierComponentOutput { + success: HiLo::from_hi_lo([zero, two]), + }; + let mut hasher = create_hasher(); + hasher.initialize_consts(builder.base.main(0), &range.gate); let pool = builder.base.pool(0); - let res = parallelize_core(pool, input, |ctx, subquery| { - let input = Self::assign_input(ctx, subquery); - handle_single_groth16verify(ctx, &range, input, params.limb_bits, params.num_limbs) - }); - ((), res) - } -} + let res = parallelize_core(pool, chunked_input, |ctx, subquery| { + let assigned_inputs = subquery + .iter() + .map(|x| Self::assign_input(ctx, x.clone())) + .collect::>(); -impl DummyFrom - for Groth16VerifierComponentInput -{ - fn dummy_from(_core_params: Groth16VerifierComponentParams) -> Self { - Groth16VerifierComponentInput { - vk: Groth16VerifierComponentVerificationKey::default(), - proof: Groth16VerifierComponentProof::default(), - public_inputs: FixLenVec::new(vec![F::ZERO; MAX_PUBLIC_INPUTS]).unwrap(), - num_public_inputs: F::from(MAX_PUBLIC_INPUTS as u64), - } + let (input, res) = + join_groth16_input(ctx, &range, &assigned_inputs, &hasher, input_constants); + let out = handle_single_groth16verify( + ctx, + &range, + input, + params.limb_bits, + params.num_limbs, + params.max_public_inputs, + ) + .1; + ctx.constrain_equal(&res, &out.success.lo()); + let last_idx = assigned_inputs.len() - 1; + let mut outputs = assigned_inputs + .iter() + .map(|x| (x.clone(), null_output.clone())) + .collect::>(); + outputs[last_idx] = (assigned_inputs[last_idx].clone(), out); + outputs + }); + let outputs = res.into_iter().flatten().collect::>(); + ((), outputs) } } -//todo: actually implement dummy from for the component params -impl DummyFrom - for Vec> -{ - fn dummy_from(_core_params: Groth16VerifierComponentParams) -> Self { - todo!() +impl DummyFrom for Vec> { + fn dummy_from(core_params: Groth16VerifierComponentParams) -> Self { + let capacity = core_params.capacity; + let constants = get_groth16_consts_from_max_pi(core_params.max_public_inputs); + assert_eq!(capacity % constants.num_chunks, 0); + let num_joined_subqueries = capacity / constants.num_chunks; + let single_input = default_groth16_subquery_input(core_params.max_public_inputs); + let stringified = serde_json::to_string(&single_input).unwrap(); + let single_input: Vec> = + serde_json::from_str(&stringified).unwrap(); + (0..num_joined_subqueries) + .flat_map(|_| single_input.clone()) + .collect::>() } } -pub fn handle_single_groth16verify( +pub fn handle_single_groth16verify( ctx: &mut Context, range: &RangeChip, - input: Groth16VerifierComponentInput, MAX_PUBLIC_INPUTS>, + input: Groth16VerifierInput>, limb_bits: usize, num_limbs: usize, + max_public_inputs: usize, ) -> ( - Groth16VerifierComponentInput, MAX_PUBLIC_INPUTS>, - Groth16VerifierComponentOutput, MAX_PUBLIC_INPUTS>, + Groth16VerifierInput>, + Groth16VerifierComponentOutput>, ) { let fp_chip = FpChip::::new(range, limb_bits, num_limbs); let fp2_chip = Fp2Chip::::new(&fp_chip); @@ -176,11 +243,9 @@ pub fn handle_single_groth16verify( range, &g1_chip, input.num_public_inputs, - MAX_PUBLIC_INPUTS + 1, + max_public_inputs + 1, ); - let public_inputs = input.public_inputs.clone(); - let success = verify_proof( ctx, range, @@ -189,8 +254,200 @@ pub fn handle_single_groth16verify( &g2_chip, &vk, &p, - &public_inputs.vec, + &input.public_inputs, + ); + + let hi_lo_success = HiLo::from_hi_lo([ctx.load_constant(F::ZERO), success]); + + ( + input, + Groth16VerifierComponentOutput { + success: hi_lo_success, + }, + ) +} + +pub fn join_groth16_input( + ctx: &mut Context, + range: &RangeChip, + chunks: &[Groth16VerifierComponentInput>], + hasher: &PoseidonHasher, + input_constants: Groth16InputConstants, +) -> (Groth16VerifierInput>, AssignedValue) { + assert_eq!(chunks.len(), input_constants.num_chunks); + let mut verification_result: AssignedValue = ctx.load_constant(F::ZERO); + let shift = ctx.load_constant(biguint_to_fe(&BigUint::from(2u64).pow(31 * 8))); + let chunks = chunks + .iter() + .enumerate() + .map(|(idx, x)| { + let mut bytes = x.packed_bytes.vec.clone(); + // this is the most significat byte in the first bytes32 chunk + let res = uint_to_bytes_be(ctx, range, &bytes[0], 32)[0]; + bytes[0] = range.gate().sub_mul(ctx, bytes[0], res, shift); + if idx == input_constants.num_chunks - 1 { + verification_result = *res; + } else { + range + .gate() + .assert_is_const(ctx, &res, &F::from(NULL_CHUNK_VAL as u64)); + } + Groth16VerifierComponentInput { + packed_bytes: bytes.into(), + } + }) + .collect::>(); + + let mut bytes: Vec> = vec![]; + let mut byte_chunks = chunks + .iter() + .flat_map(|x| x.packed_bytes.vec.clone()) + .collect::>(); + let hash = byte_chunks.pop().unwrap(); + assert_eq!( + byte_chunks.len(), + input_constants.max_num_fe_per_input_no_hash ); + let res = hasher.hash_fix_len_array(ctx, &range.gate, &byte_chunks); + ctx.constrain_equal(&res, &hash); + + let mut pis = byte_chunks + .split_off(byte_chunks.len() - input_constants.max_pi - input_constants.num_fe_padding); + let padding = pis.split_off(pis.len() - input_constants.num_fe_padding); + padding + .iter() + .for_each(|x| range.gate.assert_is_const(ctx, x, &F::ZERO)); + let mut idx = 0; + let mut unpack_bytes = |bytes: &mut Vec>, mut num_bytes: usize| { + while num_bytes > 0 { + let num_bytes_fe = min(NUM_BYTES_PER_FE, num_bytes); + let mut chunk_bytes = uint_to_bytes_le(ctx, range, &byte_chunks[idx], num_bytes_fe); + bytes.append(&mut chunk_bytes); + num_bytes -= num_bytes_fe; + idx += 1; + } + }; + unpack_bytes(&mut bytes, input_constants.num_bytes_vkey); + assert_eq!(bytes.len(), input_constants.num_bytes_vkey); + let num_inputs = bytes.pop().unwrap(); + unpack_bytes(&mut bytes, NUM_BYTES_PROOF); + assert_eq!( + idx, + input_constants.num_fe_per_input - input_constants.max_pi + ); + assert_eq!( + bytes.len(), + input_constants.num_bytes_per_input_without_pi - 1 + ); + let bytes32_chunks = bytes.chunks(32).collect::>(); + let unpacked = bytes32_chunks + .iter() + .flat_map(|x| pack_bytes_le_to_hilo(ctx, range.gate(), x).flatten()) + .collect::>(); + let vk_fe = unpacked[..input_constants.num_fe_hilo_vkey].to_vec(); + let proof_fe = unpacked[input_constants.num_fe_hilo_vkey..].to_vec(); + ( + Groth16VerifierInput { + vk: Groth16VerifierComponentVerificationKey::unflatten( + vk_fe, + input_constants.gamma_abc_g1_len, + ), + proof: Groth16VerifierComponentProof::unflatten(proof_fe).unwrap(), + public_inputs: pis, + num_public_inputs: *num_inputs.as_ref(), + }, + verification_result, + ) +} + +pub fn unflatten_groth16_input(input: Vec, max_pi: usize) -> Groth16VerifierInput { + let constants = get_groth16_consts_from_max_pi(max_pi); + let mut input = input + .chunks(NUM_FE_PER_CHUNK) + .collect::>() + .iter() + .flat_map(|x| { + let mut x = x.to_vec(); + let first_fe_bytes = x[0].to_bytes_le(); + x[0] = F::from_bytes_le(&first_fe_bytes[..31]); + x + }) + .collect::>(); + //remove the hash from the flattened input + input.pop(); + assert_eq!(input.len(), constants.max_num_fe_per_input_no_hash); + // assert_eq!(hash, compute_poseidon(&input)); + let mut public_inputs = + input.split_off(input.len() - constants.max_pi - constants.num_fe_padding); + public_inputs.truncate(public_inputs.len() - constants.num_fe_padding); + assert_eq!(public_inputs.len(), constants.max_pi); + let proof_fe = input.split_off(input.len() - NUM_FE_PROOF); + assert_eq!(proof_fe.len(), NUM_FE_PROOF); + let vk_fe = input.split_off(input.len() - constants.num_fe_vkey); + assert_eq!(vk_fe.len(), constants.num_fe_vkey); - (input, Groth16VerifierComponentOutput { success }) + let unpack_bytes = |bytes: Vec, mut num_bytes: usize| -> Vec { + let mut idx = 0; + let mut byte_chunks = vec![]; + while num_bytes > 0 { + let num_bytes_fe = min(NUM_BYTES_PER_FE, num_bytes); + let mut chunk_bytes = bytes[idx].to_bytes_le()[..num_bytes_fe].to_vec(); + byte_chunks.append(&mut chunk_bytes); + num_bytes -= num_bytes_fe; + idx += 1; + } + byte_chunks + }; + + let mut vk_bytes = unpack_bytes(vk_fe.clone(), constants.num_bytes_vkey); + let num_public_inputs = vk_bytes.pop().unwrap(); + + let proof_bytes = unpack_bytes(proof_fe.clone(), NUM_BYTES_PROOF); + + let vk_hilo = vk_bytes + .chunks(16) + .collect::>() + .iter() + .map(|x| F::from_u128(u128::from_le_bytes((*x).try_into().unwrap()))) + .collect::>(); + + let proof_hilo = proof_bytes + .chunks(16) + .collect::>() + .iter() + .map(|x| F::from_u128(u128::from_le_bytes((*x).try_into().unwrap()))) + .collect::>(); + + let vk = + Groth16VerifierComponentVerificationKey::unflatten(vk_hilo, constants.gamma_abc_g1_len); + let proof = Groth16VerifierComponentProof::unflatten(proof_hilo).unwrap(); + Groth16VerifierInput { + vk, + proof, + public_inputs, + num_public_inputs: F::from(num_public_inputs as u64), + } +} + +pub fn pack_bytes_le_to_hilo( + ctx: &mut Context, + gate: &impl GateInstructions, + bytes: &[SafeByte], +) -> HiLo> { + let len = bytes.len(); + assert!(len <= 32); + let hi = if len > 16 { + let mut hi_bytes = bytes[0..len - 16].to_vec(); + hi_bytes.reverse(); + bytes_be_to_uint(ctx, gate, &hi_bytes, hi_bytes.len()) + } else { + ctx.load_zero() + }; + let lo = { + let lo_len = if len > 16 { 16 } else { len }; + let mut lo_bytes = bytes[len - lo_len..len].to_vec(); + lo_bytes.reverse(); + bytes_be_to_uint(ctx, gate, &lo_bytes, lo_len) + }; + HiLo::from_hi_lo([hi, lo]) } diff --git a/axiom-components/src/groth16/native.rs b/axiom-components/src/groth16/native.rs new file mode 100644 index 00000000..313f696b --- /dev/null +++ b/axiom-components/src/groth16/native.rs @@ -0,0 +1,95 @@ +use axiom_eth::{ + halo2curves::{ + bn256::{Fq2, G1Affine, G2Affine}, + CurveAffine, + }, + Field, +}; +use halo2_ecc::halo2_base::utils::{biguint_to_fe, fe_to_biguint}; + +use super::{ + biguint_to_hilo, + types::Groth16VerifierInput, + verifier::{ + native::verify_groth16_native, + types::{Proof, VerifyingKey}, + }, + Groth16VerifierComponentProof, Groth16VerifierComponentVerificationKey, HiLoPair, HiLoPoint, +}; +use crate::ecdsa::utils::decode_hilo_to_biguint; + +pub fn g1_affine_to_hilo_point(point: G1Affine) -> HiLoPoint { + ( + biguint_to_hilo(fe_to_biguint(&point.x)), + biguint_to_hilo(fe_to_biguint(&point.y)), + ) +} + +pub fn hilo_point_to_g1_affine(point: HiLoPoint) -> G1Affine { + G1Affine::from_xy( + biguint_to_fe(&decode_hilo_to_biguint(point.0)), + biguint_to_fe(&decode_hilo_to_biguint(point.1)), + ) + .unwrap() +} + +pub fn g2_affine_to_hilo_pair(pair: G2Affine) -> HiLoPair { + let x_c0 = biguint_to_hilo(fe_to_biguint(&pair.x.c0)); + let x_c1 = biguint_to_hilo(fe_to_biguint(&pair.x.c1)); + let y_c0 = biguint_to_hilo(fe_to_biguint(&pair.y.c0)); + let y_c1 = biguint_to_hilo(fe_to_biguint(&pair.y.c1)); + ((x_c0, x_c1), (y_c0, y_c1)) +} + +pub fn hilo_pair_to_g2_affine(pair: HiLoPair) -> G2Affine { + G2Affine::from_xy( + Fq2 { + c0: biguint_to_fe(&decode_hilo_to_biguint(pair.0 .0)), + c1: biguint_to_fe(&decode_hilo_to_biguint(pair.0 .1)), + }, + Fq2 { + c0: biguint_to_fe(&decode_hilo_to_biguint(pair.1 .0)), + c1: biguint_to_fe(&decode_hilo_to_biguint(pair.1 .1)), + }, + ) + .unwrap() +} + +impl From> for VerifyingKey { + fn from(input: Groth16VerifierComponentVerificationKey) -> Self { + VerifyingKey { + alpha_g1: hilo_point_to_g1_affine(input.alpha_g1), + beta_g2: hilo_pair_to_g2_affine(input.beta_g2), + gamma_g2: hilo_pair_to_g2_affine(input.gamma_g2), + delta_g2: hilo_pair_to_g2_affine(input.delta_g2), + gamma_abc_g1: input + .gamma_abc_g1 + .into_iter() + .map(|pt| hilo_point_to_g1_affine(pt)) + .collect::>(), + } + } +} + +impl From> for Proof { + fn from(input: Groth16VerifierComponentProof) -> Self { + Proof { + a: hilo_point_to_g1_affine(input.a), + b: hilo_pair_to_g2_affine(input.b), + c: hilo_point_to_g1_affine(input.c), + } + } +} + +pub fn verify_groth16(input: Groth16VerifierInput, max_pi: usize) -> bool { + verify_groth16_native( + input.vk.into(), + input.proof.into(), + input + .public_inputs + .iter() + .map(|x| biguint_to_fe(&fe_to_biguint(x))) + .collect::>(), + max_pi, + ) +} diff --git a/axiom-components/src/groth16/test.rs b/axiom-components/src/groth16/test.rs index 905fffd7..dd172c60 100644 --- a/axiom-components/src/groth16/test.rs +++ b/axiom-components/src/groth16/test.rs @@ -1,20 +1,23 @@ use std::str::FromStr; -use axiom_eth::{halo2_proofs::halo2curves::bn256::Fr, utils::hilo::HiLo}; +use axiom_eth::{ + halo2_proofs::halo2curves::bn256::Fr, + snark_verifier::util::arithmetic::fe_from_big, + utils::{component::utils::compute_poseidon, hilo::HiLo}, + Field, +}; use itertools::Itertools; use lazy_static::lazy_static; +use num_bigint::BigUint; use super::{ - Groth16VerifierComponent, Groth16VerifierComponentInput, Groth16VerifierComponentOutput, + get_groth16_consts_from_max_pi, Groth16VerifierComponentInput, Groth16VerifierComponentOutput, Groth16VerifierComponentParams, Groth16VerifierComponentProof, - Groth16VerifierComponentVerificationKey, + Groth16VerifierComponentVerificationKey, Groth16VerifierInput, }; -use crate::{ - groth16::{vec_to_hilo_pair, vec_to_hilo_point, HiLoPair, HiLoPoint}, - utils::{ - flatten::{FixLenVec, VecKey}, - testing::{basic_component_outputs_test, basic_component_test_prove}, - }, +use crate::groth16::{ + native::verify_groth16, vec_to_hilo_pair, vec_to_hilo_point, HiLoPair, HiLoPoint, + NUM_FE_PER_CHUNK, NUM_FE_PROOF, }; macro_rules! deserialize_key { @@ -22,17 +25,115 @@ macro_rules! deserialize_key { serde_json::from_value($json[$val].clone()).unwrap() }; } +use crate::utils::flatten::InputFlatten; + +const DEFAULT_JSON: &str = include_str!("./test_data/default.json"); +const DEFAULT_PROOF_JSON: &str = include_str!("./test_data/default_proof.json"); +const DEFAULT_PUBLIC_INPUTS_JSON: &str = include_str!("./test_data/default_public_inputs.json"); + +pub fn flatten_groth16_input(input: Groth16VerifierInput, max_pi: usize) -> Vec { + let constants = get_groth16_consts_from_max_pi(max_pi); + let res = verify_groth16(input.clone(), constants.max_pi); + let flattened_vkey = input.vk.flatten(); + assert_eq!(constants.num_fe_hilo_vkey, flattened_vkey.len()); + let num_public_inputs = input.num_public_inputs.to_bytes_le()[0]; + let packed_vkey = bytepack(flattened_vkey, Some(vec![num_public_inputs])); + assert_eq!(packed_vkey.len(), constants.num_fe_vkey); + + let flattened_proof = input.proof.flatten_vec(); + assert_eq!(4 + 8 + 4, flattened_proof.len()); + let packed_proof = bytepack(flattened_proof, None); + assert_eq!(packed_proof.len(), NUM_FE_PROOF); + + let mut packed_fe = packed_vkey; + packed_fe.extend(packed_proof); + assert_eq!(packed_fe.len(), constants.num_fe_per_input_without_pi); + packed_fe.extend(input.public_inputs); + assert_eq!(packed_fe.len(), constants.num_fe_per_input); + packed_fe.resize_with(constants.max_num_fe_per_input_no_hash, || F::ZERO); + let hash = compute_poseidon(&packed_fe); + packed_fe.push(hash); + let packed_fe_with_res = packed_fe + .chunks(NUM_FE_PER_CHUNK) + .collect::>() + .iter() + .enumerate() + .flat_map(|(idx, x)| { + let mut bytes = x.to_vec(); + let mut arr: [u8; 32] = [0; 32]; + if idx == constants.num_chunks - 1 { + arr[31] = res as u8; + bytes[0] += F::from_bytes_le(&arr); + } else { + arr[31] = 2; + bytes[0] += F::from_bytes_le(&arr); + } + bytes + }) + .collect::>(); + packed_fe_with_res +} + +fn bytepack(input: Vec, bytes_to_add: Option>) -> Vec { + let mut bytes: Vec = input + .iter() + .flat_map(|x| { + // decompose(x, 16, 8) + x.to_bytes_le()[..16].to_vec() + }) + .collect::>(); + if let Some(bytes_to_add) = bytes_to_add { + bytes.extend(bytes_to_add); + } + bytes + .chunks(31) + .collect::>() + .iter() + .map(|x| F::from_bytes_le(x)) + .collect::>() +} + +pub fn default_groth16_input(max_pi: usize) -> Groth16VerifierInput { + parse_input( + DEFAULT_JSON.to_string(), + DEFAULT_PROOF_JSON.to_string(), + DEFAULT_PUBLIC_INPUTS_JSON.to_string(), + max_pi, + ) +} + +pub fn default_groth16_subquery_input(max_pi: usize) -> Vec> { + let input = default_groth16_input(max_pi); + let raw_chunks = flatten_groth16_input(input, max_pi); + raw_chunks + .chunks(NUM_FE_PER_CHUNK) + .map(|x| Groth16VerifierComponentInput { + packed_bytes: x.to_vec().into(), + }) + .collect_vec() +} -const MAX_PUBLIC_INPUTS: usize = 11; +pub fn read_and_parse_input( + vk_path: String, + pf_path: String, + pub_path: String, + max_pi: usize, +) -> Groth16VerifierInput { + let vk_string = std::fs::read_to_string(vk_path).unwrap(); + let pf_string = std::fs::read_to_string(pf_path).unwrap(); + let pub_string = std::fs::read_to_string(pub_path).unwrap(); + parse_input(vk_string, pf_string, pub_string, max_pi) +} -pub fn read_input( - vk_file: String, - pf_file: String, - pub_file: String, -) -> Groth16VerifierComponentInput { - let verification_key_file = std::fs::read_to_string(vk_file).unwrap(); +pub fn parse_input( + vk_string: String, + pf_string: String, + pub_string: String, + max_pi: usize, +) -> Groth16VerifierInput { + let input_constants = get_groth16_consts_from_max_pi(max_pi); let verification_key_file: serde_json::Value = - serde_json::from_str(verification_key_file.as_str()).unwrap(); + serde_json::from_str(vk_string.as_str()).unwrap(); let vk_alpha_1: [String; 3] = deserialize_key!(verification_key_file, "vk_alpha_1"); let vk_beta_2: [[String; 2]; 3] = deserialize_key!(verification_key_file, "vk_beta_2"); @@ -45,20 +146,22 @@ pub fn read_input( let delta_g2: HiLoPair = vec_to_hilo_pair(&vk_delta_2); let ic: Vec<[String; 3]> = deserialize_key!(verification_key_file, "IC"); - let mut ic_vec = ic.into_iter().map(|s| vec_to_hilo_point(&s)).collect_vec(); - ic_vec.resize(MAX_PUBLIC_INPUTS + 1, (HiLo::default(), HiLo::default())); - let gamma_abc_g1 = VecKey::new(ic_vec).unwrap(); + let mut ic_vec: Vec> = + ic.into_iter().map(|s| vec_to_hilo_point(&s)).collect_vec(); + ic_vec.resize( + input_constants.gamma_abc_g1_len, + (HiLo::default(), HiLo::default()), + ); let vk = Groth16VerifierComponentVerificationKey { alpha_g1, beta_g2, gamma_g2, delta_g2, - gamma_abc_g1, + gamma_abc_g1: ic_vec, }; - let proof_file = std::fs::read_to_string(pf_file).unwrap(); - let proof_file: serde_json::Value = serde_json::from_str(proof_file.as_str()).unwrap(); + let proof_file: serde_json::Value = serde_json::from_str(pf_string.as_str()).unwrap(); // get proof let a: [String; 3] = deserialize_key!(proof_file, "pi_a"); @@ -72,115 +175,217 @@ pub fn read_input( let pf = Groth16VerifierComponentProof { a, b, c }; // get public inputs - let public_file = std::fs::read_to_string(pub_file).unwrap(); - let public_file: serde_json::Value = serde_json::from_str(public_file.as_str()).unwrap(); + let public_file: serde_json::Value = serde_json::from_str(pub_string.as_str()).unwrap(); let pi: Vec = serde_json::from_value(public_file.clone()).unwrap(); let len = pi.len(); let mut pi = pi .into_iter() - .map(|p| Fr::from(u64::from_str(&p).unwrap())) + .map(|p| fe_from_big(BigUint::from_str(&p).unwrap())) .collect_vec(); - pi.resize(MAX_PUBLIC_INPUTS, Fr::from(0)); - let public_inputs = FixLenVec::new(pi).unwrap(); + pi.resize(input_constants.max_pi, Fr::from(0)); - Groth16VerifierComponentInput { + Groth16VerifierInput { vk, proof: pf, - public_inputs, + public_inputs: pi, num_public_inputs: Fr::from(len as u64 + 1), } } -fn get_groth16_output(success: bool) -> Groth16VerifierComponentOutput { +pub fn get_groth16_output(val: u64) -> Groth16VerifierComponentOutput { Groth16VerifierComponentOutput { - success: if success { Fr::one() } else { Fr::zero() }, + success: HiLo::from_hi_lo([Fr::zero(), Fr::from(val)]), } } lazy_static! { static ref GROTH16VERIFY_PARAMS: Groth16VerifierComponentParams = Groth16VerifierComponentParams { - capacity: 1, + capacity: 3, limb_bits: 88, num_limbs: 3, + max_public_inputs: 4, }; } lazy_static! { static ref GROTH16VERIFY_PARAMS_CAP2: Groth16VerifierComponentParams = Groth16VerifierComponentParams { - capacity: 2, + capacity: 6, limb_bits: 88, num_limbs: 3, + max_public_inputs: 4, }; } -#[test] -fn test_groth16_output() { - basic_component_outputs_test::>( - 20, - vec![read_input( - "src/groth16/test_data/puzzle.json".to_string(), - "src/groth16/test_data/proof.json".to_string(), - "src/groth16/test_data/public_inputs.json".to_string(), - )], - vec![get_groth16_output(true)], - GROTH16VERIFY_PARAMS.clone(), - ); -} +#[cfg(test)] +mod tests { + use axiom_eth::utils::build_utils::dummy::DummyFrom; -#[test] -fn test_groth16_output_default() { - basic_component_outputs_test::>( - 20, - vec![read_input( + use super::*; + use crate::{ + groth16::{native::verify_groth16, unflatten_groth16_input, Groth16VerifierComponent}, + utils::testing::basic_component_outputs_test, + }; + #[test] + fn test_groth16_output() { + let params = GROTH16VERIFY_PARAMS.clone(); + let input = read_and_parse_input( "src/groth16/test_data/default.json".to_string(), "src/groth16/test_data/default_proof.json".to_string(), "src/groth16/test_data/default_public_inputs.json".to_string(), - )], - vec![get_groth16_output(true)], - GROTH16VERIFY_PARAMS.clone(), - ); -} + params.max_public_inputs, + ); + let raw_chunks = flatten_groth16_input(input, params.max_public_inputs); + let chunks = raw_chunks + .chunks(NUM_FE_PER_CHUNK) + .map(|x| Groth16VerifierComponentInput { + packed_bytes: x.to_vec().into(), + }) + .collect_vec(); + basic_component_outputs_test::>( + 20, + chunks, + vec![ + get_groth16_output(2), + get_groth16_output(2), + get_groth16_output(1), + ], + GROTH16VERIFY_PARAMS.clone(), + ); + } -#[test] -fn test_groth16_output_with_wrong_signature() { - basic_component_outputs_test::>( - 20, - vec![read_input( - "src/groth16/test_data/puzzle_modified.json".to_string(), - "src/groth16/test_data/proof.json".to_string(), - "src/groth16/test_data/public_inputs_modified.json".to_string(), - )], - vec![get_groth16_output(false)], - GROTH16VERIFY_PARAMS.clone(), - ); -} + #[test] + fn test_groth16_output_wrong_signature() { + let params = GROTH16VERIFY_PARAMS.clone(); + let input = read_and_parse_input( + "src/groth16/test_data/default.json".to_string(), + "src/groth16/test_data/default_proof.json".to_string(), + "src/groth16/test_data/default_public_inputs_modified.json".to_string(), + params.max_public_inputs, + ); + let raw_chunks = flatten_groth16_input(input, params.max_public_inputs); + let chunks = raw_chunks + .chunks(NUM_FE_PER_CHUNK) + .map(|x| Groth16VerifierComponentInput { + packed_bytes: x.to_vec().into(), + }) + .collect_vec(); + basic_component_outputs_test::>( + 20, + chunks, + vec![ + get_groth16_output(2), + get_groth16_output(2), + get_groth16_output(0), + ], + GROTH16VERIFY_PARAMS.clone(), + ); + } -#[test] -fn test_groth16_prove() { - basic_component_test_prove::>( - 20, - vec![read_input( + #[test] + fn test_groth16_output_more_pi() { + let mut params = GROTH16VERIFY_PARAMS.clone(); + params.max_public_inputs = 15; + let constants = get_groth16_consts_from_max_pi(params.max_public_inputs); + params.capacity = constants.num_chunks; + let mut outputs = vec![get_groth16_output(2); constants.num_chunks]; + outputs[constants.num_chunks - 1] = get_groth16_output(1); + let input = read_and_parse_input( "src/groth16/test_data/puzzle.json".to_string(), "src/groth16/test_data/proof.json".to_string(), "src/groth16/test_data/public_inputs.json".to_string(), - )], - GROTH16VERIFY_PARAMS.clone(), - ) - .unwrap(); -} + params.max_public_inputs, + ); + let raw_chunks = flatten_groth16_input(input, params.max_public_inputs); + let chunks = raw_chunks + .chunks(NUM_FE_PER_CHUNK) + .map(|x| Groth16VerifierComponentInput { + packed_bytes: x.to_vec().into(), + }) + .collect_vec(); + basic_component_outputs_test::>( + 20, + chunks, + outputs, + params.clone(), + ); + } + + #[test] + fn test_groth16_dummy_from_input() { + let params = GROTH16VERIFY_PARAMS_CAP2.clone(); + let chunks = > as DummyFrom< + Groth16VerifierComponentParams, + >>::dummy_from(params.clone()); + basic_component_outputs_test::>( + 20, + chunks, + vec![ + get_groth16_output(2), + get_groth16_output(2), + get_groth16_output(1), + get_groth16_output(2), + get_groth16_output(2), + get_groth16_output(1), + ], + params, + ); + } + + #[test] + #[should_panic] + fn test_groth16_join_output_fail() { + let input = Groth16VerifierComponentInput { + packed_bytes: vec![Fr::from(0); NUM_FE_PER_CHUNK].into(), + }; + basic_component_outputs_test::>( + 20, + vec![input; 3], + vec![ + get_groth16_output(2), + get_groth16_output(2), + get_groth16_output(0), + ], + GROTH16VERIFY_PARAMS.clone(), + ); + } -#[test] -fn test_groth16_prove_default() { - basic_component_test_prove::>( - 20, - vec![read_input( + #[test] + fn test_native_verify() { + let input = read_and_parse_input( "src/groth16/test_data/default.json".to_string(), "src/groth16/test_data/default_proof.json".to_string(), "src/groth16/test_data/default_public_inputs.json".to_string(), - )], - GROTH16VERIFY_PARAMS.clone(), - ) - .unwrap(); + 4, + ); + let res = verify_groth16(input, 4); + assert!(res); + } + + #[test] + fn test_flatten_unflatten() { + let input = read_and_parse_input( + "src/groth16/test_data/default.json".to_string(), + "src/groth16/test_data/default_proof.json".to_string(), + "src/groth16/test_data/default_public_inputs.json".to_string(), + 4, + ); + let raw_chunks = flatten_groth16_input(input.clone(), 4); + let unflattened = unflatten_groth16_input(raw_chunks, 4); + assert_eq!(input, unflattened); + } + + #[test] + fn test_flatten_unflatten_more_pi() { + let input = read_and_parse_input( + "src/groth16/test_data/puzzle.json".to_string(), + "src/groth16/test_data/proof.json".to_string(), + "src/groth16/test_data/public_inputs.json".to_string(), + 15, + ); + let raw_chunks = flatten_groth16_input(input.clone(), 15); + let unflattened = unflatten_groth16_input(raw_chunks, 15); + assert_eq!(input, unflattened); + } } diff --git a/axiom-components/src/groth16/test_data/default_public_inputs_modified.json b/axiom-components/src/groth16/test_data/default_public_inputs_modified.json new file mode 100644 index 00000000..09e08618 --- /dev/null +++ b/axiom-components/src/groth16/test_data/default_public_inputs_modified.json @@ -0,0 +1,4 @@ +[ + "385", + "10" +] \ No newline at end of file diff --git a/axiom-components/src/groth16/types.rs b/axiom-components/src/groth16/types.rs new file mode 100644 index 00000000..a079fc0f --- /dev/null +++ b/axiom-components/src/groth16/types.rs @@ -0,0 +1,179 @@ +use axiom_eth::{ + utils::{ + component::circuit::{CoreBuilderOutputParams, CoreBuilderParams}, + encode_h256_to_hilo, + hilo::HiLo, + }, + zkevm_hashes::util::eth_types::ToLittleEndian, + Field, +}; +use component_derive::ComponentIO; +use ethers_core::types::{BigEndianHash, H256}; +use serde::{Deserialize, Serialize}; + +use super::{HiLoPair, HiLoPoint}; +use crate::{ + ecdsa::utils::decode_hilo_to_h256, + groth16::NUM_FE_PER_CHUNK, + utils::flatten::{FixLenVec, InputFlatten}, +}; + +#[derive(Default, Clone, Serialize, Deserialize)] +pub struct Groth16VerifierComponentParams { + pub capacity: usize, + pub limb_bits: usize, + pub num_limbs: usize, + pub max_public_inputs: usize, +} + +impl CoreBuilderParams for Groth16VerifierComponentParams { + fn get_output_params(&self) -> CoreBuilderOutputParams { + CoreBuilderOutputParams::new(vec![self.capacity]) + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Groth16VerifierComponentVerificationKey { + pub alpha_g1: HiLoPoint, + pub beta_g2: HiLoPair, + pub gamma_g2: HiLoPair, + pub delta_g2: HiLoPair, + pub gamma_abc_g1: Vec>, +} + +impl Groth16VerifierComponentVerificationKey { + pub fn unflatten(vec: Vec, gamma_abc_g1_len: usize) -> Self { + let mut iter = vec.into_iter(); + let alpha_g1_fe: Vec = iter + .by_ref() + .take( as InputFlatten>::NUM_FE) + .collect(); + let alpha_g1 = HiLoPoint::unflatten(alpha_g1_fe).unwrap(); + + let beta_g2_fe: Vec = iter + .by_ref() + .take( as InputFlatten>::NUM_FE) + .collect(); + let beta_g2 = HiLoPair::unflatten(beta_g2_fe).unwrap(); + + let gamma_g2_fe: Vec = iter + .by_ref() + .take( as InputFlatten>::NUM_FE) + .collect(); + let gamma_g2 = HiLoPair::unflatten(gamma_g2_fe).unwrap(); + + let delta_g2_fe: Vec = iter + .by_ref() + .take( as InputFlatten>::NUM_FE) + .collect(); + let delta_g2 = HiLoPair::unflatten(delta_g2_fe).unwrap(); + + let gamma_abc_g1: Vec> = (0..gamma_abc_g1_len) + .map(|_| { + let fe: Vec = iter + .by_ref() + .take( as InputFlatten>::NUM_FE) + .collect(); + HiLoPoint::unflatten(fe).unwrap() + }) + .collect(); + + Self { + alpha_g1, + beta_g2, + gamma_g2, + delta_g2, + gamma_abc_g1, + } + } + + pub fn flatten(&self) -> Vec { + let mut vec: Vec = Vec::new(); + let flattened_alpha_g1: Vec = self.alpha_g1.flatten_vec(); + vec.extend(flattened_alpha_g1); + let flattened_beta_g2: Vec = self.beta_g2.flatten_vec(); + vec.extend(flattened_beta_g2); + let flattened_gamma_g2: Vec = self.gamma_g2.flatten_vec(); + vec.extend(flattened_gamma_g2); + let flattened_delta_g2: Vec = self.delta_g2.flatten_vec(); + vec.extend(flattened_delta_g2); + for pt in &self.gamma_abc_g1 { + let flattened_pt: Vec = pt.flatten_vec(); + vec.extend(flattened_pt); + } + vec + } +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] +pub struct Groth16VerifierComponentProof { + pub a: HiLoPoint, + pub b: HiLoPair, + pub c: HiLoPoint, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct Groth16VerifierInput { + pub vk: Groth16VerifierComponentVerificationKey, + pub proof: Groth16VerifierComponentProof, + pub num_public_inputs: T, + pub public_inputs: Vec, // MAX_PUBLIC_INPUTS +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO, Default)] +pub struct Groth16VerifierComponentInput { + pub packed_bytes: FixLenVec, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct Groth16NativeInput { + pub bytes: Vec, +} + +#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, ComponentIO)] +pub struct Groth16VerifierComponentOutput { + pub success: HiLo, +} + +impl From for Groth16VerifierComponentInput { + fn from(input: Groth16NativeInput) -> Self { + let bytes = input + .bytes + .iter() + .map(|x| F::from_bytes_le(&x.into_uint().to_le_bytes())) + .collect(); + Self { + packed_bytes: FixLenVec::new(bytes).unwrap(), + } + } +} + +impl From> for Groth16NativeInput { + fn from(input: Groth16VerifierComponentInput) -> Self { + let bytes = input + .packed_bytes + .vec + .into_iter() + .map(|x| { + let mut bytes = x.to_repr(); + bytes.reverse(); + H256::from_slice(&bytes) + }) + .collect(); + Self { bytes } + } +} + +impl From> for H256 { + fn from(value: Groth16VerifierComponentOutput) -> Self { + decode_hilo_to_h256(value.success) + } +} + +impl From for Groth16VerifierComponentOutput { + fn from(value: H256) -> Self { + Groth16VerifierComponentOutput { + success: encode_h256_to_hilo(&value), + } + } +} diff --git a/axiom-components/src/groth16/utils.rs b/axiom-components/src/groth16/utils.rs index cc5520e4..dd3d5cb2 100644 --- a/axiom-components/src/groth16/utils.rs +++ b/axiom-components/src/groth16/utils.rs @@ -5,17 +5,17 @@ use axiom_eth::{ utils::hilo::HiLo, Field, }; -use groth_verifier::types::*; use halo2_ecc::{bn254::FpChip, ecc::EccChip, fields::vector::FieldVector}; use num_bigint::BigUint; use num_traits::One; +use super::verifier::types::{G1AffineAssigned, G2AffineAssigned}; use crate::ecdsa::utils::load_fp_from_hilo; // HiLoPoint represents a point on the G1 curve -// and HiLoPair represents a point on the G2 curve +// and HiLoPair represents a point on the G2 curve pub type HiLoPoint = (HiLo, HiLo); -pub type HiLoPair = (HiLoPoint, HiLoPoint); +pub type HiLoPair = (HiLoPoint, HiLoPoint); pub fn hilo_point_to_affine( ctx: &mut Context, @@ -33,7 +33,7 @@ pub fn hilo_pair_to_affine( ctx: &mut Context, range: &RangeChip, g1_chip: &EccChip>, - pair: HiLoPair> + pair: HiLoPair>, ) -> G2AffineAssigned { let fp_chip = g1_chip.field_chip(); let bx0 = load_fp_from_hilo(ctx, range, fp_chip, pair.0 .0); @@ -54,8 +54,8 @@ pub fn biguint_to_hilo(x: BigUint) -> HiLo { pub fn vec_to_hilo_point(arr: &[String]) -> HiLoPoint { ( - biguint_to_hilo(FromStr::from_str(&arr[0]).unwrap()), - biguint_to_hilo(FromStr::from_str(&arr[1]).unwrap()) + biguint_to_hilo(FromStr::from_str(&arr[0]).unwrap()), + biguint_to_hilo(FromStr::from_str(&arr[1]).unwrap()), ) } diff --git a/axiom-components/src/groth16/verifier/mod.rs b/axiom-components/src/groth16/verifier/mod.rs new file mode 100644 index 00000000..5227314a --- /dev/null +++ b/axiom-components/src/groth16/verifier/mod.rs @@ -0,0 +1,97 @@ +pub mod native; +#[cfg(test)] +pub mod test; +pub mod types; + +use axiom_eth::{ + halo2_base::{ + gates::{GateInstructions, RangeChip, RangeInstructions}, + halo2_proofs::halo2curves::bn256::G1Affine, + utils::BigPrimeField, + AssignedValue, Context, + }, + utils::circuit_utils::unsafe_lt_mask, +}; +use halo2_ecc::{ + bn254::{pairing::PairingChip, Fp12Chip, Fp2Chip, FpChip}, + ecc::EccChip, + fields::FieldChip, +}; +use types::{G1AffineAssigned, ProofAssigned, VerifyingKeyAssigned}; + +/// Prepare proof inputs for use with [`verify_proof_with_prepared_inputs`], wrt the prepared +/// verification key `vk` and instance public inputs. +pub fn prepare_inputs( + ctx: &mut Context, + range: &RangeChip, + g1_chip: &EccChip>, + vk: &VerifyingKeyAssigned, + public_inputs: &[AssignedValue], +) -> G1AffineAssigned { + assert!((public_inputs.len() + 1) == vk.gamma_abc_g1.len()); + let gate = range.gate(); + range.check_less_than_safe(ctx, vk.abc_len, vk.gamma_abc_g1.len() as u64 + 1); + // check abc_len != 0 + let len_is_zero = gate.is_zero(ctx, vk.abc_len); + gate.assert_is_const(ctx, &len_is_zero, &F::ZERO); + let one = ctx.load_constant(F::ONE); + let num_pi = gate.sub(ctx, vk.abc_len, one); + let lt_mask = unsafe_lt_mask(ctx, gate, num_pi, public_inputs.len()); + let mut addends = vec![vk.gamma_abc_g1[0].clone()]; + for (j, (i, b)) in public_inputs + .iter() + .zip(vk.gamma_abc_g1.iter().skip(1)) + .enumerate() + { + let i_mask = gate.mul(ctx, lt_mask[j], *i); + let bi = g1_chip.scalar_mult::(ctx, (*b).clone(), vec![i_mask], 254, 4); + addends.push(bi); + } + g1_chip.sum::(ctx, addends) +} + +/// Verify a Groth16 proof `proof` against the prepared verification key `pvk` and prepared public +/// inputs. This should be preferred over [`verify_proof`] if the instance's public inputs are +/// known in advance. +pub fn verify_proof_with_prepared_inputs( + ctx: &mut Context, + pairing_chip: &PairingChip, + g2_chip: &EccChip>, + vk: &VerifyingKeyAssigned, + proof: &ProofAssigned, + prepared_inputs: &G1AffineAssigned, +) -> AssignedValue { + let neg_gamma_g2 = g2_chip.negate(ctx, &vk.gamma_g2); + let neg_delta_g2 = g2_chip.negate(ctx, &vk.delta_g2); + let qap = pairing_chip.multi_miller_loop( + ctx, + vec![ + (&prepared_inputs, &neg_gamma_g2), + (&proof.c, &neg_delta_g2), + (&proof.a, &proof.b), + ], + ); + let test = pairing_chip.final_exp(ctx, qap); + let alpha_g1_beta_g2 = pairing_chip.pairing(ctx, &vk.beta_g2, &vk.alpha_g1); + let fp12_chip = Fp12Chip::new(g2_chip.field_chip().fp_chip()); + // println!("FIRST: {:?}\n", test); + // println!("FIRST: {:?}\n", alpha_g1_beta_g2); + fp12_chip.is_equal(ctx, &test, &alpha_g1_beta_g2) +} + +/// Verify a Groth16 proof `proof` against the prepared verification key `pvk`, +/// with respect to the instance `public_inputs`. +#[allow(clippy::too_many_arguments)] +pub fn verify_proof( + ctx: &mut Context, + range: &RangeChip, + pairing_chip: &PairingChip, + g1_chip: &EccChip>, + g2_chip: &EccChip>, + vk: &VerifyingKeyAssigned, + proof: &ProofAssigned, + public_inputs: &[AssignedValue], +) -> AssignedValue { + let prepared_inputs = prepare_inputs(ctx, range, g1_chip, vk, public_inputs); + verify_proof_with_prepared_inputs(ctx, pairing_chip, g2_chip, vk, proof, &prepared_inputs) +} diff --git a/axiom-components/src/groth16/verifier/native.rs b/axiom-components/src/groth16/verifier/native.rs new file mode 100644 index 00000000..b2e07598 --- /dev/null +++ b/axiom-components/src/groth16/verifier/native.rs @@ -0,0 +1,35 @@ +use std::ops::{Add, Mul, Neg}; + +use axiom_eth::halo2_base::halo2_proofs::halo2curves::{ + bn256::{multi_miller_loop, Bn256, Fr, G1}, + pairing::{Engine, MillerLoopResult}, +}; + +use super::types::{Proof, VerifyingKey}; + +pub fn verify_groth16_native(vk: VerifyingKey, proof: Proof, pi: Vec, max_pi: usize) -> bool { + let mut vk = vk.clone(); + vk.gamma_abc_g1.resize(max_pi + 1, vk.gamma_abc_g1[0]); + + let alpha_g1_beta_g2 = Bn256::pairing(&vk.alpha_g1, &vk.beta_g2); + + let mut pi = pi.clone(); + pi.resize(max_pi, Fr::zero()); + + let prepared_inputs = { + let mut g_ic: G1 = vk.gamma_abc_g1[0].into(); + for (i, b) in pi.iter().zip(vk.gamma_abc_g1.iter().skip(1)) { + g_ic = g_ic.add(b.mul(i)); + } + g_ic + }; + + let test = multi_miller_loop(&[ + (&proof.a, &proof.b.into()), + (&prepared_inputs.into(), &vk.gamma_g2.neg().into()), + (&proof.c, &vk.delta_g2.neg().into()), + ]) + .final_exponentiation(); + + test == alpha_g1_beta_g2 +} diff --git a/axiom-components/src/groth16/verifier/test.rs b/axiom-components/src/groth16/verifier/test.rs new file mode 100644 index 00000000..a2e07125 --- /dev/null +++ b/axiom-components/src/groth16/verifier/test.rs @@ -0,0 +1,172 @@ +use std::str::FromStr; + +use axiom_eth::{ + halo2_base::{ + gates::RangeChip, + halo2_proofs::halo2curves::bn256::{Fq2, G1Affine, G2Affine}, + utils::{testing::base_test, BigPrimeField}, + Context, + }, + zkevm_hashes::util::eth_types::ToLittleEndian, +}; +use ethers_core::types::U256; +use halo2_ecc::{ + bn254::{pairing::PairingChip, Fp2Chip, FpChip}, + ecc::EccChip, +}; +use itertools::Itertools; +use num_bigint::{self, BigUint}; + +use super::{ + types::{Proof, VerifyingKey}, + verify_proof, +}; + +pub fn string_to_g1(s: String) -> [u64; 4] { + let mut arr = [0; 4]; + let mut big_int = BigUint::from_str(&s).unwrap(); + let modulus: u128 = 1 << 64; + for item in &mut arr { + *item = (big_int.clone() % modulus).try_into().unwrap(); + big_int -= *item; + big_int /= modulus; + } + arr +} + +pub fn string_to_g2(s: String, r: String) -> ([u64; 4], [u64; 4]) { + (string_to_g1(s), string_to_g1(r)) +} + +pub fn vec_to_g1(s: [String; 3]) -> G1Affine { + let s = s.into_iter().map(string_to_g1).collect_vec(); + G1Affine { + x: s[0].into(), + y: s[1].into(), + } +} + +pub fn vec_to_g2(s: [[String; 2]; 3]) -> G2Affine { + let s = s + .into_iter() + .map(|s| string_to_g2(s[0].clone(), s[1].clone())) + .collect_vec(); + G2Affine { + x: Fq2 { + c0: s[0].0.into(), + c1: s[0].1.into(), + }, + y: Fq2 { + c0: s[1].0.into(), + c1: s[1].1.into(), + }, + } +} + +pub fn read_input(path: String, path2: String, path3: String) -> (VerifyingKey, Proof, Vec) { + let pf_str = std::fs::read_to_string(path).unwrap(); + let pf: serde_json::Value = serde_json::from_str(pf_str.as_str()).unwrap(); + let vk_alpha_1: [String; 3] = serde_json::from_value(pf["vk_alpha_1"].clone()).unwrap(); + let vk_beta_2: [[String; 2]; 3] = serde_json::from_value(pf["vk_beta_2"].clone()).unwrap(); + let vk_gamma_2: [[String; 2]; 3] = serde_json::from_value(pf["vk_gamma_2"].clone()).unwrap(); + let vk_delta_2: [[String; 2]; 3] = serde_json::from_value(pf["vk_delta_2"].clone()).unwrap(); + let alpha_g1 = vec_to_g1(vk_alpha_1); + let beta_g2 = vec_to_g2(vk_beta_2); + let gamma_g2 = vec_to_g2(vk_gamma_2); + let delta_g2 = vec_to_g2(vk_delta_2); + let ic: Vec<[String; 3]> = serde_json::from_value(pf["IC"].clone()).unwrap(); + let gamma_abc_g1 = ic.into_iter().map(|s| vec_to_g1(s.clone())).collect_vec(); + let vk = VerifyingKey { + alpha_g1, + beta_g2, + gamma_g2, + delta_g2, + gamma_abc_g1, + }; + let pf_str = std::fs::read_to_string(path2).unwrap(); + let pf: serde_json::Value = serde_json::from_str(pf_str.as_str()).unwrap(); + let a: [String; 3] = serde_json::from_value(pf["pi_a"].clone()).unwrap(); + let b: [[String; 2]; 3] = serde_json::from_value(pf["pi_b"].clone()).unwrap(); + let c: [String; 3] = serde_json::from_value(pf["pi_c"].clone()).unwrap(); + let a = vec_to_g1(a); + let b = vec_to_g2(b); + let c = vec_to_g1(c); + let proof = Proof { a, b, c }; + let pf_str = std::fs::read_to_string(path3).unwrap(); + let pf: serde_json::Value = serde_json::from_str(pf_str.as_str()).unwrap(); + let pi: Vec = serde_json::from_value(pf.clone()).unwrap(); + let pi = pi + .into_iter() + .map(|p| U256::from_str(p.as_str()).unwrap()) + .collect_vec(); + (vk, proof, pi) +} + +#[test] +pub fn read_puzzle_input() { + read_input( + "src/groth16/verifier/test_data/puzzle.json".to_string(), + "src/groth16/verifier/test_data/proof.json".to_string(), + "src/groth16/verifier/test_data/public_inputs.json".to_string(), + ); +} + +#[allow(clippy::too_many_arguments)] +fn basic_g1_tests( + ctx: &mut Context, + range: &RangeChip, + limb_bits: usize, + num_limbs: usize, + vk: VerifyingKey, + proof: Proof, + public_inputs: Vec, + max_len: usize, +) { + let fp_chip = FpChip::::new(range, limb_bits, num_limbs); + let fp2_chip = Fp2Chip::::new(&fp_chip); + let g1_chip = EccChip::new(&fp_chip); + let g2_chip = EccChip::new(&fp2_chip); + let pairing_chip = PairingChip::new(&fp_chip); + let p = proof.assign(ctx, &g1_chip, &g2_chip); + let vk = vk.assign(ctx, &g1_chip, &g2_chip, max_len + 1); + let mut public_inputs = public_inputs + .into_iter() + .map(|p| ctx.load_witness(F::from_bytes_le(&p.to_le_bytes()))) + .collect_vec(); + let zero = ctx.load_witness(F::from(0)); + public_inputs.resize(max_len, zero); + verify_proof( + ctx, + range, + &pairing_chip, + &g1_chip, + &g2_chip, + &vk, + &p, + &public_inputs, + ); +} + +#[test] +fn test_puzzle() { + base_test().k(20).lookup_bits(19).run(|ctx, range| { + let (vk, proof, public_inputs) = read_input( + "src/groth16/verifier/test_data/puzzle.json".to_string(), + "src/groth16/verifier/test_data/proof.json".to_string(), + "src/groth16/verifier/test_data/public_inputs.json".to_string(), + ); + basic_g1_tests(ctx, range, 88, 3, vk, proof, public_inputs, 20); + }); +} + +#[test] +fn test_default() { + base_test().k(20).lookup_bits(19).run(|ctx, range| { + let (vk, proof, public_inputs) = read_input( + "src/groth16/verifier/test_data/default.json".to_string(), + "src/groth16/verifier/test_data/default_proof.json".to_string(), + "src/groth16/verifier/test_data/default_public_inputs.json".to_string(), + ); + basic_g1_tests(ctx, range, 88, 3, vk, proof, public_inputs, 20); + }); +} diff --git a/axiom-components/src/groth16/verifier/test_data/default.json b/axiom-components/src/groth16/verifier/test_data/default.json new file mode 100644 index 00000000..86d5ac7e --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/default.json @@ -0,0 +1,99 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 2, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "3467004866717634776339573347423169002547635002985878815508898961522743506452", + "19865331138541507750394553392171827109124121759749012695741881283803108508347" + ], + [ + "2189040294157466407187767498553323210668670372829423239655416479671231034454", + "9060993844443059227358689399147552366673505348777583132029617367152835581644" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "18635637960557560857094512684047887771596119327447282264110915689478359176884", + "2787739109285944951079006253283606975566487445507222876760682453501866793407", + "1" + ], + [ + "5924520914420601398188001890215477493578059223690495899194579533928493711633", + "10458548929339196911259801917183441320857030738864516659971366674948828826220", + "1" + ], + [ + "700433635233066042387364180517373526251183001161528242738155819104314938031", + "5917976447441905930232643207659393469418720332008787407970690068286695117993", + "1" + ] + ] +} \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/test_data/default_proof.json b/axiom-components/src/groth16/verifier/test_data/default_proof.json new file mode 100644 index 00000000..a7facfc5 --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/default_proof.json @@ -0,0 +1,28 @@ +{ + "pi_a": [ + "8680517028102508686697833871754756412796114974847015697141940673533053840142", + "11141740546280350879096131061836524403590156119612784562000052415659458825489", + "1" + ], + "pi_b": [ + [ + "1440891193609311978764021485940260427550408426443948025356285139253983656114", + "394601482735020272172278930519584503435819882918609688334533005392757855204" + ], + [ + "20494951949987457972693384400773181275472641495294869490116939117698276584704", + "10308299421766040557229866656950534872860339113790990257433934972338427371767" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "15673413881925436037817830665630792674139737175244287780191582909763810965191", + "15925053412344238109167921622693154977078903172031475553777735264199083434300", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/test_data/default_public_inputs.json b/axiom-components/src/groth16/verifier/test_data/default_public_inputs.json new file mode 100644 index 00000000..20bcc187 --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/default_public_inputs.json @@ -0,0 +1,4 @@ +[ + "385", + "5" +] \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/test_data/proof.json b/axiom-components/src/groth16/verifier/test_data/proof.json new file mode 100644 index 00000000..639687d0 --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/proof.json @@ -0,0 +1,28 @@ +{ + "pi_a": [ + "962423428081562910901059144273450716544121041259586243377276693905507059495", + "6269920170316908075332568210114679585341557519371079712445757779930945262488", + "1" + ], + "pi_b": [ + [ + "3473546770835491692959700059780143623805915270641221528428153874725348976522", + "21682306474032690305208973865406962638362599403795888996124023885056643621936" + ], + [ + "7879676437216769559260035791177908989880202205086660019060147485090308560994", + "5959841779051761543175716123558168850061269357803176163494356782589776655729" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "6442167158077394342423479055624667285312664150565764656182370204000970612507", + "8419072572960491648316888824475879537159658539985586234540725052311960683330", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/test_data/public_inputs.json b/axiom-components/src/groth16/verifier/test_data/public_inputs.json new file mode 100644 index 00000000..d6635c5d --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/public_inputs.json @@ -0,0 +1,13 @@ +[ + "2", + "0", + "42", + "0", + "93", + "0", + "0", + "0", + "0", + "0", + "0" +] \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/test_data/puzzle.json b/axiom-components/src/groth16/verifier/test_data/puzzle.json new file mode 100644 index 00000000..f9a6de67 --- /dev/null +++ b/axiom-components/src/groth16/verifier/test_data/puzzle.json @@ -0,0 +1,144 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 11, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "1301440655462093245373106360799083795679765904685639582722491593797402927781", + "17270602004643109894840507932129928847986959895556589085593770125596184179253" + ], + [ + "7278453974459869831319652276588135268214457204781099311602054961995150138030", + "20418515439689032165082990135653730520348201748084898057163411226452716243365" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "18167771208750482521664353324581113888786036463728198018832762940102508097002", + "17711170233362048290109207474322791677468986701017872546245578800991296094696", + "1" + ], + [ + "966318491506385384433820986812083268212622165270177861148356454778869002232", + "12114772687954634880011360513716141889810582679914666097428323992707649859104", + "1" + ], + [ + "18393657841510818459469230522772996059530981572945018454314108550626631617527", + "276334269663650328837397605522283156034503830349805250057780405888201213112", + "1" + ], + [ + "16361238224044463679028850957619268678224328152031202067600851935504132392092", + "8115289544716230745249872822550627587849486188059323095337402735638528534943", + "1" + ], + [ + "4501021300713040170128100282694205933215643868499056087516268985299692248186", + "8866650512592555474100861234944456371229573811986557277208611135301752464969", + "1" + ], + [ + "8751169105335636126636423485234847719377678968608523360912563425089513697203", + "18354904053207603032473804602849348199773049772911728463407024994416832078772", + "1" + ], + [ + "19041090949071767298121095835413695230901989091063431413912328949275529571269", + "1273859052217812649967290238850120065610027013280338876204936664135645867320", + "1" + ], + [ + "8287897639050195051852241102875516544560479336810940411726368968479279693299", + "10493560368612830610822803054766919101097826808855218458157472156591039824339", + "1" + ], + [ + "4118005374697876124133431815759892172945263502984254652155368432421062299260", + "18826221436630695308561191755571537965013809579866827080510241464368990881657", + "1" + ], + [ + "16626554046938633513345160443980820123598827257126507810481396073938757816988", + "5629263397341214830175029039454296059147936147078195333329216799882697934812", + "1" + ], + [ + "9921961020773465365163240017772384831076363116743061634790600644627436670127", + "8619602004893756663896331306750419481660616421746429181660238728010382543104", + "1" + ], + [ + "6813321130173844295182009954144572636402406965910222794496456473506943506945", + "5122397177857741038527713786520363152191319777204337927775026534156845690456", + "1" + ] + ] +} \ No newline at end of file diff --git a/axiom-components/src/groth16/verifier/types.rs b/axiom-components/src/groth16/verifier/types.rs new file mode 100644 index 00000000..9dbdf0ce --- /dev/null +++ b/axiom-components/src/groth16/verifier/types.rs @@ -0,0 +1,122 @@ +use axiom_eth::halo2_base::{ + halo2_proofs::halo2curves::bn256::{G1Affine, G2Affine}, + utils::BigPrimeField, + AssignedValue, Context, +}; +use halo2_ecc::{ + bigint::ProperCrtUint, + bn254::{Fp2Chip, FpChip}, + ecc::{EcPoint, EccChip}, + fields::vector::FieldVector, +}; +use serde::{Deserialize, Serialize}; + +pub type G2AffineAssigned = EcPoint>>; +pub type G1AffineAssigned = EcPoint>; + +/// A proof in the Groth16 SNARK. +#[derive(Clone, Debug, PartialEq, Copy, Serialize, Deserialize, Eq)] +pub struct Proof { + /// The `A` element in `G1`. + pub a: G1Affine, + /// The `B` element in `G2`. + pub b: G2Affine, + /// The `C` element in `G1`. + pub c: G1Affine, +} + +/// A verification key in the Groth16 SNARK. +#[derive(Clone, Debug)] +pub struct ProofAssigned { + /// The `A` element in `G1`. + pub a: G1AffineAssigned, + /// The `B` element in `G2`. + pub b: G2AffineAssigned, + /// The `C` element in `G1`. + pub c: G1AffineAssigned, +} + +impl Proof { + pub fn assign( + &self, + ctx: &mut Context, + g1_chip: &EccChip>, + g2_chip: &EccChip>, + ) -> ProofAssigned { + // let fp_chip = FpChip::::new(&range, params.limb_bits, params.num_limbs); + // let fp2_chip = Fp2Chip::::new(&fp_chip); + let a = g1_chip.assign_point(ctx, self.a); + let b = g2_chip.assign_point(ctx, self.b); + let c = g1_chip.assign_point(ctx, self.c); + + ProofAssigned { a, b, c } + } +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/// A verification key in the Groth16 SNARK. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default, Eq)] +pub struct VerifyingKey { + /// The `alpha * G`, where `G` is the generator of `E::G1`. + pub alpha_g1: G1Affine, + /// The `alpha * H`, where `H` is the generator of `E::G2`. + pub beta_g2: G2Affine, + /// The `gamma * H`, where `H` is the generator of `E::G2`. + pub gamma_g2: G2Affine, + /// The `delta * H`, where `H` is the generator of `E::G2`. + pub delta_g2: G2Affine, + /// The `gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * H`, where `H` is the generator of `E::G1`. + pub gamma_abc_g1: Vec, +} + +/// A verification key in the Groth16 SNARK. +#[derive(Clone, Debug)] +pub struct VerifyingKeyAssigned { + /// The `alpha * G`, where `G` is the generator of `E::G1`. + pub alpha_g1: G1AffineAssigned, + /// The `alpha * H`, where `H` is the generator of `E::G2`. + pub beta_g2: G2AffineAssigned, + /// The `gamma * H`, where `H` is the generator of `E::G2`. + pub gamma_g2: G2AffineAssigned, + /// The `delta * H`, where `H` is the generator of `E::G2`. + pub delta_g2: G2AffineAssigned, + /// The `gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * H`, where `H` is the generator of `E::G1`. + pub gamma_abc_g1: Vec>, + + pub abc_len: AssignedValue, +} + +impl VerifyingKey { + pub fn assign( + &self, + ctx: &mut Context, + g1_chip: &EccChip>, + g2_chip: &EccChip>, + max_len: usize, + ) -> VerifyingKeyAssigned { + // let fp_chip = FpChip::::new(&range, params.limb_bits, params.num_limbs); + // let fp2_chip = Fp2Chip::::new(&fp_chip); + let alpha_g1 = g1_chip.assign_point(ctx, self.alpha_g1); + let beta_g2 = g2_chip.assign_point(ctx, self.beta_g2); + let gamma_g2 = g2_chip.assign_point(ctx, self.gamma_g2); + let delta_g2 = g2_chip.assign_point(ctx, self.delta_g2); + let len = self.gamma_abc_g1.len(); + let mut gamma_abc_g1 = self + .gamma_abc_g1 + .iter() + .map(|pt| g1_chip.assign_point(ctx, *pt)) + .collect::>(); + let abc_len = ctx.load_witness(F::from(len as u64)); + gamma_abc_g1.resize(max_len, gamma_abc_g1[0].clone()); + VerifyingKeyAssigned { + alpha_g1, + beta_g2, + gamma_g2, + delta_g2, + gamma_abc_g1, + abc_len, + } + } +} diff --git a/axiom-components/src/lib.rs b/axiom-components/src/lib.rs index b7f83c76..ebf2a861 100644 --- a/axiom-components/src/lib.rs +++ b/axiom-components/src/lib.rs @@ -1,13 +1,12 @@ #![feature(associated_type_defaults)] #![feature(associated_type_bounds)] -#![feature(return_position_impl_trait_in_trait)] #![allow(incomplete_features)] #![feature(generic_const_exprs)] pub use axiom_eth::utils::component as framework; pub use halo2_ecc; pub mod ecdsa; mod example; -// pub mod groth16; mod example_generics; +pub mod groth16; pub mod scaffold; pub mod utils; diff --git a/axiom-components/src/scaffold/mod.rs b/axiom-components/src/scaffold/mod.rs index 24517d74..210da23f 100644 --- a/axiom-components/src/scaffold/mod.rs +++ b/axiom-components/src/scaffold/mod.rs @@ -89,7 +89,7 @@ pub trait BasicComponentScaffold: BasicComponentScaffoldIOTypes { impl + 'static> ComponentType for BasicComponentScaffoldImpl where - I::Input: LogicalInputValue + DummyFrom, + I::Input: LogicalInputValue, { type InputValue = I::Input; type InputWitness = I::Input>; @@ -144,7 +144,7 @@ impl> ComponentBuilder impl + 'static> CoreBuilder for BasicComponentScaffoldImpl where - I::Input: LogicalInputValue + DummyFrom, + I::Input: LogicalInputValue, Vec>: DummyFrom, { type CompType = Self; @@ -161,7 +161,7 @@ where capacity ); } - input.resize(capacity, input.get(0).unwrap().clone()); + input.resize(capacity, input.first().unwrap().clone()); self.input = Some(input); Ok(()) } diff --git a/axiom-components/src/utils/testing.rs b/axiom-components/src/utils/testing.rs index 5a62635e..0939f71e 100644 --- a/axiom-components/src/utils/testing.rs +++ b/axiom-components/src/utils/testing.rs @@ -72,7 +72,7 @@ pub fn basic_component_outputs_test + 'static>( expected_output: Vec< as ComponentType>::OutputValue>, component_params: I::Params, ) where - I::Input: LogicalInputValue + DummyFrom, + I::Input: LogicalInputValue, Vec>: DummyFrom, { let rlc_circuit_params = RlcCircuitParams { @@ -106,7 +106,7 @@ pub fn basic_component_test_prove + 'static>( component_params: >::Params, ) -> anyhow::Result<()> where - I::Input: LogicalInputValue + DummyFrom, + I::Input: LogicalInputValue, Vec>: DummyFrom, { let rlc_circuit_params = RlcCircuitParams { diff --git a/axiom-eth/Cargo.toml b/axiom-eth/Cargo.toml index 1c32ac7f..c1983731 100644 --- a/axiom-eth/Cargo.toml +++ b/axiom-eth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axiom-eth" -version = "0.4.2" +version = "0.4.3" authors = ["Intrinsic Technologies"] license = "MIT" edition = "2021" diff --git a/axiom-eth/src/utils/component/circuit/comp_circuit_impl.rs b/axiom-eth/src/utils/component/circuit/comp_circuit_impl.rs index f5f487f0..0f185f98 100644 --- a/axiom-eth/src/utils/component/circuit/comp_circuit_impl.rs +++ b/axiom-eth/src/utils/component/circuit/comp_circuit_impl.rs @@ -360,16 +360,22 @@ impl, P: PromiseBuilder> Circuit { let mut core_builder = self.core_builder.borrow_mut(); let mut promise_builder = self.promise_builder.borrow_mut(); - let rlc_builder = self.rlc_builder.borrow_mut(); + let mut rlc_builder = self.rlc_builder.borrow_mut(); let mut phase0_layouter = layouter.namespace(|| "raw synthesize phase0"); core_builder.borrow_mut().raw_synthesize_phase0(&config.0, &mut phase0_layouter); promise_builder.raw_synthesize_phase0(&config.1, &mut phase0_layouter); rlc_builder.raw_synthesize_phase0(&config.2, phase0_layouter); - } - #[cfg(feature = "halo2-axiom")] - { - layouter.next_phase(); + + #[cfg(feature = "halo2-axiom")] + { + if rlc_builder.witness_gen_only() { + // To save memory, clear virtual columns in phase0 because they should never be used again + rlc_builder.base.pool(0).threads.clear(); + } + drop(rlc_builder); + layouter.next_phase(); + } } self.rlc_builder .borrow_mut() diff --git a/axiom-eth/src/utils/component/promise_collector.rs b/axiom-eth/src/utils/component/promise_collector.rs index 6d64401d..974c63db 100644 --- a/axiom-eth/src/utils/component/promise_collector.rs +++ b/axiom-eth/src/utils/component/promise_collector.rs @@ -121,21 +121,19 @@ impl PromiseCollector { self.witness_grouped_calls .iter() .map(|(type_id, calls)| { - ( - type_id.clone(), - calls - .iter() - .flat_map(|(_, calls_per_context)| { - calls_per_context.iter().map(|(p, _)| TypelessPromiseCall { - capacity: p.get_capacity(), - logical_input: p.to_typeless_logical_input(), - }) - }) - .sorted() // sorting to ensure the order is deterministic. - // Note: likely not enough promise calls to be worth using par_sort - .dedup() - .collect_vec(), - ) + let calls = calls.iter().flat_map(|(_, calls_per_context)| { + calls_per_context.iter().map(|(p, _)| TypelessPromiseCall { + capacity: p.get_capacity(), + logical_input: p.to_typeless_logical_input(), + }) + }); + dbg!(type_id.as_str()); + let calls = if type_id.as_str() != "axiom-query:VirtualComponentTypeResultsRoot" { + calls.sorted().dedup().collect_vec() + } else { + calls.dedup().collect_vec() + }; + (type_id.clone(), calls) }) .collect() } diff --git a/axiom-query/Cargo.toml b/axiom-query/Cargo.toml index 0910160f..1c83e07f 100644 --- a/axiom-query/Cargo.toml +++ b/axiom-query/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axiom-query" -version = "2.1.0-alpha.1" +version = "2.1.0-alpha.2" authors = ["Intrinsic Technologies"] license = "MIT" edition = "2021" @@ -35,7 +35,7 @@ rand = "0.8" rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } # halo2, features turned on by axiom-eth -axiom-eth = { version = "=0.4.2", path = "../axiom-eth", default-features = false, features = ["providers", "aggregation", "evm"] } +axiom-eth = { version = "0.4.3", path = "../axiom-eth", default-features = false, features = ["providers", "aggregation", "evm"] } axiom-codec = { version = "0.3.0-alpha", path = "../axiom-codec", default-features = false } axiom-components = { path = "../axiom-components" } diff --git a/axiom-query/configs/test/results_root.json b/axiom-query/configs/test/results_root.json index 104bc10d..92fc5958 100644 --- a/axiom-query/configs/test/results_root.json +++ b/axiom-query/configs/test/results_root.json @@ -3,8 +3,8 @@ "base": { "k": 18, "num_advice_per_phase": [ - 32, - 1 + 33, + 2 ], "num_fixed": 1, "num_lookup_advice_per_phase": [ @@ -47,12 +47,15 @@ 262134, 262133, 262133, - 262132, - 262133, 262133, - 262134 + 262134, + 262134, + 262134, + 262132 ], - [] + [ + 262132 + ] ], "rlc": [] } diff --git a/axiom-query/src/components/results/circuit.rs b/axiom-query/src/components/results/circuit.rs index 18423d09..fd552b40 100644 --- a/axiom-query/src/components/results/circuit.rs +++ b/axiom-query/src/components/results/circuit.rs @@ -1,5 +1,5 @@ use axiom_codec::constants::{MAX_SUBQUERY_OUTPUTS, NUM_SUBQUERY_TYPES}; -use axiom_components::ecdsa::ComponentTypeECDSA; +use axiom_components::{ecdsa::ComponentTypeECDSA, groth16::ComponentTypeGroth16Verifier}; use axiom_eth::{ component_type_list, halo2_base::{ @@ -56,7 +56,7 @@ pub struct CoreBuilderResultsRoot { } /// Specify the output format of ResultRoot component. -#[derive(Clone, Default, Serialize, Deserialize)] +#[derive(Clone, Default, Serialize)] pub struct CoreParamsResultRoot { /// - `enabled_types[subquery_type as u16]` is true if the subquery type is enabled. /// - `enabled_types[0]` corresponds to the Null type. It doesn't matter whether it's enabled or disabled; behavior remains the same. @@ -64,6 +64,31 @@ pub struct CoreParamsResultRoot { /// Maximum total number of subquery results supported pub capacity: usize, } + +impl<'de> Deserialize<'de> for CoreParamsResultRoot { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct TempCoreParamsResultRoot { + enabled_types: Vec, + capacity: usize, + } + + let temp = TempCoreParamsResultRoot::deserialize(deserializer)?; + if temp.enabled_types.len() > NUM_SUBQUERY_TYPES { + panic!("enabled_types length exceeds NUM_SUBQUERY_TYPES"); + } + let mut enabled_types = [false; NUM_SUBQUERY_TYPES]; + for (i, &value) in temp.enabled_types.iter().enumerate() { + enabled_types[i] = value; + } + + Ok(CoreParamsResultRoot { enabled_types, capacity: temp.capacity }) + } +} + impl CoreBuilderParams for CoreParamsResultRoot { fn get_output_params(&self) -> CoreBuilderOutputParams { CoreBuilderOutputParams::new(vec![]) @@ -79,7 +104,8 @@ pub type SubqueryDependencies = component_type_list!( ComponentTypeTxSubquery, ComponentTypeReceiptSubquery, ComponentTypeSolidityNestedMappingSubquery, - ComponentTypeECDSA + ComponentTypeECDSA, + ComponentTypeGroth16Verifier ); pub type PromiseLoaderResultsRoot = PromiseBuilderCombo< @@ -176,8 +202,14 @@ impl CoreBuilder for CoreBuilderResultsRoot { let mut poseidon = create_hasher(); poseidon.initialize_consts(ctx, gate); // compute resultsRootPoseidon - let results_root_poseidon = - get_results_root(ctx, range, &poseidon, &subqueries, num_subqueries); + let results_root_poseidon = get_results_root( + ctx, + range, + &poseidon, + &subqueries, + num_subqueries, + &self.params.enabled_types, + ); let commit_subquery_hashes = { // take variable length list of `num_subqueries` subquery hashes and flat hash them all together diff --git a/axiom-query/src/components/results/results_root.rs b/axiom-query/src/components/results/results_root.rs index 6eefdbe3..dbd46e22 100644 --- a/axiom-query/src/components/results/results_root.rs +++ b/axiom-query/src/components/results/results_root.rs @@ -1,3 +1,4 @@ +use axiom_codec::constants::NUM_SUBQUERY_TYPES; use axiom_eth::{ halo2_base::{ gates::{GateInstructions, RangeChip, RangeInstructions}, @@ -39,11 +40,20 @@ pub fn get_results_root( poseidon: &PoseidonHasher, results: &[AssignedSubqueryResult], num_subqueries: AssignedValue, + enabled_types: &[bool; NUM_SUBQUERY_TYPES], ) -> AssignedValue { let gate = range.gate(); let subquery_mask = unsafe_lt_mask(ctx, gate, num_subqueries, results.len()); - get_results_root_poseidon(ctx, range, poseidon, results, num_subqueries, &subquery_mask) + get_results_root_poseidon( + ctx, + range, + poseidon, + results, + num_subqueries, + &subquery_mask, + enabled_types, + ) } /// The subquery data and results vector `subquery_results` may be resized to some fixed length @@ -77,6 +87,7 @@ pub fn get_results_root_poseidon( subquery_results: &[AssignedSubqueryResult], num_subqueries: AssignedValue, subquery_mask: &[SafeBool], + enabled_types: &[bool; NUM_SUBQUERY_TYPES], ) -> AssignedValue { let gate = range.gate(); @@ -90,7 +101,7 @@ pub fn get_results_root_poseidon( let mut leaves = Vec::with_capacity(1 << tree_depth); for (subquery_result, &mask) in subquery_results.iter().zip_eq(subquery_mask) { let key = &subquery_result.key; - let key_len = get_num_fe_from_subquery_key(ctx, gate, key); + let key_len = get_num_fe_from_subquery_key(ctx, gate, key, enabled_types); let subquery_hash = initialized_hasher.hash_var_len_array(ctx, range, &key.0, key_len); let concat = [&[subquery_hash], &subquery_result.value[..]].concat(); let mut leaf = initialized_hasher.hash_fix_len_array(ctx, gate, &concat); diff --git a/axiom-query/src/components/results/tests.rs b/axiom-query/src/components/results/tests.rs index 84d572da..07762030 100644 --- a/axiom-query/src/components/results/tests.rs +++ b/axiom-query/src/components/results/tests.rs @@ -17,8 +17,14 @@ use axiom_codec::{ }, utils::native::u256_to_h256, }; -use axiom_components::ecdsa::{ - utils::testing::custom_parameters_ecdsa, ComponentTypeECDSA, ECDSAComponentNativeInput, +use axiom_components::{ + ecdsa::{ + utils::testing::custom_parameters_ecdsa, ComponentTypeECDSA, ECDSAComponentNativeInput, + }, + groth16::{ + test::default_groth16_subquery_input, types::Groth16NativeInput, + ComponentTypeGroth16Verifier, + }, }; use axiom_eth::{ block_header::{EXTRA_DATA_INDEX, RECEIPT_ROOT_INDEX, STATE_ROOT_INDEX, TX_ROOT_INDEX}, @@ -83,12 +89,13 @@ pub struct ComponentCapacities { pub receipt: usize, pub solidity_mapping: usize, pub ecdsa: usize, + pub groth16: usize, pub keccak: usize, } impl ComponentCapacities { pub fn to_str_short(&self) -> String { format!( - "{}_{}_{}_{}_{}_{}_{}_{}_{}", + "{}_{}_{}_{}_{}_{}_{}_{}_{}_{}", self.total, self.header, self.account, @@ -97,6 +104,7 @@ impl ComponentCapacities { self.receipt, self.solidity_mapping, self.ecdsa, + self.groth16, self.keccak ) } @@ -190,6 +198,16 @@ pub fn get_test_input( let mut ecdsa_subqueries: Vec<(ECDSAComponentNativeInput, H256)> = vec![(custom_parameters_ecdsa(3, 10898, 2).into(), u256_to_h256(&U256::from_str("1")?))]; + fn get_h256(val: u64) -> H256 { + u256_to_h256(&U256::from_str(val.to_string().as_str()).unwrap()) + } + let groth16_input = default_groth16_subquery_input(4); + let mut groth16_subqueries: Vec<(Groth16NativeInput, H256)> = vec![ + (groth16_input[0].clone().into(), get_h256(2)), + (groth16_input[1].clone().into(), get_h256(2)), + (groth16_input[2].clone().into(), get_h256(1)), + ]; + header_subqueries.truncate(capacity.header); acct_subqueries.truncate(capacity.account); storage_subqueries.truncate(capacity.storage); @@ -197,6 +215,7 @@ pub fn get_test_input( tx_subqueries.truncate(capacity.tx); receipt_subqueries.truncate(capacity.receipt); ecdsa_subqueries.truncate(capacity.ecdsa); + groth16_subqueries.truncate(capacity.groth16); fn append( results: &mut Vec, @@ -222,6 +241,7 @@ pub fn get_test_input( append(&mut results, &storage_subqueries); append(&mut results, &solidity_mapping_subqueries); append(&mut results, &ecdsa_subqueries); + append(&mut results, &groth16_subqueries); results.truncate(capacity.total); let num_subqueries = results.len(); resize_with_first(&mut results, capacity.total); @@ -240,6 +260,7 @@ pub fn get_test_input( resize_with_first(&mut receipt_subqueries, capacity.receipt); resize_with_first(&mut solidity_mapping_subqueries, capacity.solidity_mapping); resize_with_first(&mut ecdsa_subqueries, capacity.ecdsa); + resize_with_first(&mut groth16_subqueries, capacity.groth16); let promise_header = prepare(header_subqueries); let promise_account = prepare(acct_subqueries); @@ -248,6 +269,7 @@ pub fn get_test_input( let promise_receipt = prepare(receipt_subqueries); let promise_solidity_mapping = prepare(solidity_mapping_subqueries); let promise_ecdsa = prepare(ecdsa_subqueries); + let promise_groth16 = prepare(groth16_subqueries); let mut promise_results = HashMap::new(); for (type_id, pr) in SubqueryDependencies::::get_component_type_ids().into_iter().zip_eq([ @@ -272,6 +294,9 @@ pub fn get_test_input( shard_into_component_promise_results::>( promise_ecdsa.convert_into(), ), + shard_into_component_promise_results::>( + promise_groth16.convert_into(), + ), ]) { // filter out empty shards with capacity = 0. if !pr.shards()[0].1.is_empty() { @@ -301,6 +326,7 @@ pub const fn test_capacity() -> ComponentCapacities { receipt: 8, solidity_mapping: 8, ecdsa: 8, + groth16: 3, keccak: 500, } } @@ -421,6 +447,7 @@ fn test_mock_results_root_header_only_for_agg() -> anyhow::Result<()> { receipt: 0, solidity_mapping: 0, ecdsa: 0, + groth16: 0, keccak: 200, }; let (input, output, mut promise_results) = get_test_input(capacity)?; diff --git a/axiom-query/src/keygen/agg/single_type.rs b/axiom-query/src/keygen/agg/single_type.rs index d2ede6c3..5eeedf25 100644 --- a/axiom-query/src/keygen/agg/single_type.rs +++ b/axiom-query/src/keygen/agg/single_type.rs @@ -20,7 +20,7 @@ use crate::keygen::{ ShardIntentAccount, ShardIntentHeader, ShardIntentReceipt, ShardIntentResultsRoot, ShardIntentSolidityMapping, ShardIntentStorage, ShardIntentTx, }, - ProvingKeySerializer, ShardIntentECDSA, SupportedPinning, + ProvingKeySerializer, ShardIntentECDSA, ShardIntentGroth16, SupportedPinning, }; use super::{common::parse_agg_intent, SupportedAggPinning}; @@ -35,6 +35,7 @@ pub enum SupportedIntentTreeSingleType { Receipt(IntentTreeSingleType), SolidityMapping(IntentTreeSingleType), ECDSA(IntentTreeSingleType), + Groth16(IntentTreeSingleType), ResultsRoot(IntentTreeSingleType), } diff --git a/axiom-query/src/keygen/agg/subquery_agg.rs b/axiom-query/src/keygen/agg/subquery_agg.rs index 330d32e5..e6700811 100644 --- a/axiom-query/src/keygen/agg/subquery_agg.rs +++ b/axiom-query/src/keygen/agg/subquery_agg.rs @@ -53,6 +53,7 @@ pub enum SubqueryAggInputSnark { Receipt, SolidityMapping, ECDSA, + Groth16, ResultsRoot, } @@ -121,6 +122,7 @@ impl KeygenAggregationCircuitIntent for SubqueryAggIntent { let snark_receipt = remove_and_wrap(&SubqueryAggInputSnark::Receipt); let snark_solidity_mapping = remove_and_wrap(&SubqueryAggInputSnark::SolidityMapping); let snark_ecdsa = remove_and_wrap(&SubqueryAggInputSnark::ECDSA); + let snark_groth16 = remove_and_wrap(&SubqueryAggInputSnark::Groth16); let promise_commit_keccak = Fr::zero(); // just a dummy let input = InputSubqueryAggregation { @@ -131,6 +133,7 @@ impl KeygenAggregationCircuitIntent for SubqueryAggIntent { snark_tx, snark_receipt, snark_ecdsa, + snark_groth16, promise_commit_keccak, snark_results_root: snark_results_root.unwrap(), }; @@ -169,6 +172,7 @@ fn get_key(supported: &SupportedIntentTreeSingleType) -> SubqueryAggInputSnark { S::SolidityMapping(_) => I::SolidityMapping, S::ResultsRoot(_) => I::ResultsRoot, S::ECDSA(_) => I::ECDSA, + S::Groth16(_) => I::Groth16, } } diff --git a/axiom-query/src/keygen/shard/mod.rs b/axiom-query/src/keygen/shard/mod.rs index d8b24a28..0f2c235e 100644 --- a/axiom-query/src/keygen/shard/mod.rs +++ b/axiom-query/src/keygen/shard/mod.rs @@ -2,7 +2,7 @@ use std::{any::TypeId, path::Path}; use axiom_components::{ ecdsa::ECDSAComponent, framework::promise_loader::empty::EmptyPromiseLoader, - scaffold::BasicComponentScaffoldImpl, + groth16::Groth16VerifierComponent, scaffold::BasicComponentScaffoldImpl, }; use axiom_eth::{ halo2_base::{ @@ -100,6 +100,10 @@ pub type ShardIntentECDSA = ComponentShardCircuitIntent< BasicComponentScaffoldImpl>, EmptyPromiseLoader, >; +pub type ShardIntentGroth16 = ComponentShardCircuitIntent< + BasicComponentScaffoldImpl>, + EmptyPromiseLoader, +>; pub type ShardIntentResultsRoot = ComponentShardCircuitIntent, PromiseLoaderResultsRoot>; // You should never shard verify compute, but the struct is the same. @@ -111,6 +115,11 @@ pub type ECDSAComponentImpl = ComponentCircuitImpl< BasicComponentScaffoldImpl>, EmptyPromiseLoader, >; +pub type Groth16ComponentImpl = ComponentCircuitImpl< + Fr, + BasicComponentScaffoldImpl>, + EmptyPromiseLoader, +>; #[derive(Clone, Serialize, Deserialize)] #[enum_dispatch(AggTreePinning)] @@ -122,6 +131,7 @@ pub enum SupportedShardPinning { ShardReceipt(ComponentShardPinning>), ShardSolidityMapping(ComponentShardPinning>), ShardECDSA(ComponentShardPinning), + ShardGroth16(ComponentShardPinning), ShardResultsRoot(ComponentShardPinning>), ShardKeccak(ComponentShardPinning>), ShardVerifyCompute(ComponentShardPinning), diff --git a/axiom-query/src/lib.rs b/axiom-query/src/lib.rs index 9bda4ee5..1022430b 100644 --- a/axiom-query/src/lib.rs +++ b/axiom-query/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(io_error_other)] #![feature(associated_type_defaults)] pub use axiom_codec; diff --git a/axiom-query/src/subquery_aggregation/circuit.rs b/axiom-query/src/subquery_aggregation/circuit.rs index 8b6771b7..ecc7329b 100644 --- a/axiom-query/src/subquery_aggregation/circuit.rs +++ b/axiom-query/src/subquery_aggregation/circuit.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use anyhow::{bail, Result}; -use axiom_components::ecdsa::ComponentTypeECDSA; +use axiom_components::{ecdsa::ComponentTypeECDSA, groth16::ComponentTypeGroth16Verifier}; use axiom_eth::{ halo2_base::gates::{circuit::CircuitBuilderStage, GateChip}, halo2_proofs::poly::kzg::commitment::ParamsKZG, @@ -52,7 +52,7 @@ impl InputSubqueryAggregation { if self.snark_solidity_mapping.is_some() && self.snark_storage.is_none() { bail!("SolidityMapping snark requires Storage snark"); } - const NUM_SNARKS: usize = 8; + const NUM_SNARKS: usize = 9; let snarks = vec![ Some(self.snark_header), self.snark_account, @@ -61,6 +61,7 @@ impl InputSubqueryAggregation { self.snark_receipt, self.snark_solidity_mapping, self.snark_ecdsa, + self.snark_groth16, Some(self.snark_results_root), ]; let snarks_enabled = snarks.iter().map(|s| s.is_some()).collect_vec(); @@ -72,6 +73,7 @@ impl InputSubqueryAggregation { ComponentTypeReceiptSubquery::::get_type_id(), ComponentTypeSolidityNestedMappingSubquery::::get_type_id(), as axiom_components::framework::ComponentType>::get_type_id(), + as axiom_components::framework::ComponentType>::get_type_id(), ]; if snarks.iter().flatten().any(|s| s.agg_vk_hash_idx.is_some()) { bail!("[SubqueryAggregation] No snark should be universal."); diff --git a/axiom-query/src/subquery_aggregation/tests.rs b/axiom-query/src/subquery_aggregation/tests.rs index eb54bc39..e6e020a3 100644 --- a/axiom-query/src/subquery_aggregation/tests.rs +++ b/axiom-query/src/subquery_aggregation/tests.rs @@ -201,6 +201,7 @@ fn get_test_input(params: &ParamsKZG) -> anyhow::Result anyhow::Result<()> { snark_tx: None, snark_receipt: None, snark_ecdsa: None, + snark_groth16: None, promise_commit_keccak: Default::default(), }; let mut circuit = diff --git a/axiom-query/src/subquery_aggregation/types.rs b/axiom-query/src/subquery_aggregation/types.rs index 02b499fe..dbd059f5 100644 --- a/axiom-query/src/subquery_aggregation/types.rs +++ b/axiom-query/src/subquery_aggregation/types.rs @@ -33,6 +33,8 @@ pub struct InputSubqueryAggregation { pub snark_receipt: Option, #[serde(skip_serializing_if = "Option::is_none")] pub snark_ecdsa: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub snark_groth16: Option, /// The keccak commit is provided as a public input. /// The SubqueryAggregation circuit will check that all subquery component circuits use the same promise commit for keccak. diff --git a/axiom-query/src/utils/codec.rs b/axiom-query/src/utils/codec.rs index 28783232..5c3b4ac5 100644 --- a/axiom-query/src/utils/codec.rs +++ b/axiom-query/src/utils/codec.rs @@ -1,4 +1,5 @@ use axiom_codec::{ + constants::NUM_SUBQUERY_TYPES, encoder::field_elements::{ FIELD_ENCODED_SOLIDITY_NESTED_MAPPING_DEPTH_IDX, NUM_FE_ANY, NUM_FE_SOLIDITY_NESTED_MAPPING_WITHOUT_KEYS, @@ -50,9 +51,12 @@ pub fn get_num_fe_from_subquery_key( ctx: &mut Context, gate: &impl GateInstructions, key: &AssignedSubqueryKey, + enabled_types: &[bool; NUM_SUBQUERY_TYPES], ) -> AssignedValue { // num_fe_by_type will include 1 field element for the type itself, while NUM_FE_ANY does not - let num_fe_by_type: Vec<_> = NUM_FE_ANY + let last_enabled_type = enabled_types.iter().rposition(|&x| x).unwrap(); + let enabled_num_fe_any = &NUM_FE_ANY[..=last_enabled_type]; + let num_fe_by_type: Vec<_> = enabled_num_fe_any .iter() .enumerate() .map(|(subtype, &num_fe)| { diff --git a/axiom-query/src/verify_compute/circuit.rs b/axiom-query/src/verify_compute/circuit.rs index 4daeed1a..630bc477 100644 --- a/axiom-query/src/verify_compute/circuit.rs +++ b/axiom-query/src/verify_compute/circuit.rs @@ -3,6 +3,7 @@ use std::iter::zip; use anyhow::bail; use axiom_codec::{ constants::{MAX_SUBQUERY_OUTPUTS, USER_RESULT_FIELD_ELEMENTS, USER_RESULT_LEN_BYTES}, + encoder::field_elements::DEFAULT_V2_ENABLED_TYPES, types::field_elements::SUBQUERY_RESULT_LEN, HiLo, }; @@ -312,6 +313,8 @@ impl CoreBuilder for CoreBuilderVerifyCompute { 0, ); + let enabled_types = self.params.enabled_types().unwrap_or(DEFAULT_V2_ENABLED_TYPES); + // not optimal: recomputing spec even though PromiseLoader also uses hasher let mut poseidon = create_hasher(); poseidon.initialize_consts(ctx, range.gate()); @@ -325,6 +328,7 @@ impl CoreBuilder for CoreBuilderVerifyCompute { &table.rows, num_subqueries, &subquery_mask, + &enabled_types, ) }; let promise_subquery_hashes = { diff --git a/axiom-query/src/verify_compute/tests/aggregation.rs b/axiom-query/src/verify_compute/tests/aggregation.rs index 8348c02b..d5105fd4 100644 --- a/axiom-query/src/verify_compute/tests/aggregation.rs +++ b/axiom-query/src/verify_compute/tests/aggregation.rs @@ -131,6 +131,7 @@ fn get_test_input( agg_params.get_g()[0], client_metadata, agg_compute_snark.protocol.preprocessed.len(), + None, ); println!("agg_compute_snark.protocol.preprocessed.len(): {}", core_params.preprocessed_len()); diff --git a/axiom-query/src/verify_compute/tests/mod.rs b/axiom-query/src/verify_compute/tests/mod.rs index d9b5cbd3..afc5e86f 100644 --- a/axiom-query/src/verify_compute/tests/mod.rs +++ b/axiom-query/src/verify_compute/tests/mod.rs @@ -182,7 +182,7 @@ fn test_verify_no_compute_mock() { let circuit_k = 19u32; let (core_params, input) = - CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &gen_srs(DUMMY_USER_K)) + CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &gen_srs(DUMMY_USER_K), None) .unwrap(); let circuit = prepare_mock_circuit(core_params, circuit_k as usize, 200, input); let instances = circuit.get_public_instances(); @@ -231,7 +231,7 @@ fn test_verify_compute_mock() -> anyhow::Result<()> { let circuit_k = 19u32; let (core_params, input) = - CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &app_params)?; + CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &app_params, None)?; let circuit = prepare_mock_circuit(core_params, circuit_k as usize, 200, input); let instances = circuit.get_public_instances(); @@ -299,6 +299,7 @@ fn test_verify_compute_prover_full() -> anyhow::Result<()> { "verify_compute", None, 200, + None, ) .unwrap() }); @@ -337,7 +338,7 @@ fn test_verify_compute_prepare_for_agg() -> anyhow::Result<()> { let circuit_k = 19u32; let (core_params, input) = - CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &app_params)?; + CircuitInputVerifyCompute::reconstruct(logic_input.clone(), &app_params, None)?; let keccak_cap = 200; let circuit = prepare_mock_circuit(core_params, circuit_k as usize, keccak_cap, input); let keccak_shard = generate_keccak_shards_from_calls(&circuit, keccak_cap)?; @@ -400,6 +401,7 @@ fn test_verify_compute_prover_for_agg() -> anyhow::Result<()> { "verify_compute_for_agg", Some(promise_keccak), keccak_cap, + None, )?; Ok(()) } @@ -419,6 +421,7 @@ impl CircuitInputVerifyCompute { pub fn reconstruct( input: InputVerifyCompute, params_for_dummy: &ParamsKZG, + enabled_types: Option<[bool; NUM_SUBQUERY_TYPES]>, ) -> anyhow::Result<(CoreParamsVerifyCompute, Self)> { let InputVerifyCompute { source_chain_id, subquery_results, compute_query } = input; @@ -435,6 +438,7 @@ impl CircuitInputVerifyCompute { params_for_dummy.get_g()[0], client_metadata, compute_snark.protocol.preprocessed.len(), + enabled_types, ); println!( "compute_snark.protocol.preprocessed.len(): {}", diff --git a/axiom-query/src/verify_compute/tests/prove.rs b/axiom-query/src/verify_compute/tests/prove.rs index ebc323ad..b9f71156 100644 --- a/axiom-query/src/verify_compute/tests/prove.rs +++ b/axiom-query/src/verify_compute/tests/prove.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, fs}; -use axiom_codec::constants::USER_MAX_OUTPUTS; +use axiom_codec::constants::{NUM_SUBQUERY_TYPES, USER_MAX_OUTPUTS}; use axiom_eth::{ halo2_base::utils::fs::gen_srs, keccak::{ @@ -34,6 +34,7 @@ pub fn verify_compute_prover( name: &str, promise_keccak: Option, keccak_capacity: usize, + enabled_types: Option<[bool; NUM_SUBQUERY_TYPES]>, ) -> anyhow::Result<(Snark, RlcCircuitPinning, OutputKeccakShard)> { type I = CircuitInputVerifyCompute; @@ -51,7 +52,7 @@ pub fn verify_compute_prover( 0, 0, )?; - let (core_params, input) = I::reconstruct(input, &default_params)?; + let (core_params, input) = I::reconstruct(input, &default_params, enabled_types)?; let circuit_k = 19u32; @@ -90,7 +91,7 @@ pub fn verify_compute_prover( logic_input.subquery_results.results.resize(max_num_subqueries, first); let first = logic_input.subquery_results.subquery_hashes[0]; logic_input.subquery_results.subquery_hashes.resize(max_num_subqueries, first); - let (core_params, input) = I::reconstruct(logic_input, &default_params)?; + let (core_params, input) = I::reconstruct(logic_input, &default_params, enabled_types)?; let circuit = ComponentCircuitVerifyCompute::prover(core_params, loader_params, pinning.clone()); circuit.feed_input(Box::new(input)).unwrap(); diff --git a/axiom-query/src/verify_compute/types.rs b/axiom-query/src/verify_compute/types.rs index 2bf6bf4f..253465a6 100644 --- a/axiom-query/src/verify_compute/types.rs +++ b/axiom-query/src/verify_compute/types.rs @@ -20,6 +20,7 @@ use axiom_eth::{ use getset::{CopyGetters, Getters}; use serde::{Deserialize, Serialize}; +use crate::axiom_codec::constants::NUM_SUBQUERY_TYPES; use crate::{ components::results::{table::SubqueryResultsTable, types::CircuitOutputResultsRoot}, utils::client_circuit::{metadata::AxiomV2CircuitMetadata, vkey::OnchainVerifyingKey}, @@ -48,6 +49,10 @@ pub struct CoreParamsVerifyCompute { /// Length of `preprocessed` in `PlonkProtocol` #[getset(get_copy = "pub")] preprocessed_len: usize, + /// - `enabled_types[subquery_type as u16]` is true if the subquery type is enabled. + /// - `enabled_types[0]` corresponds to the Null type. It doesn't matter whether it's enabled or disabled; behavior remains the same. + #[getset(get_copy = "pub")] + enabled_types: Option<[bool; NUM_SUBQUERY_TYPES]>, } impl CoreParamsVerifyCompute { @@ -56,8 +61,9 @@ impl CoreParamsVerifyCompute { svk: G1Affine, client_metadata: AxiomV2CircuitMetadata, preprocessed_len: usize, + enabled_types: Option<[bool; NUM_SUBQUERY_TYPES]>, ) -> Self { - Self { subquery_results_capacity, svk, client_metadata, preprocessed_len } + Self { subquery_results_capacity, svk, client_metadata, preprocessed_len, enabled_types } } } impl CoreBuilderParams for CoreParamsVerifyCompute { diff --git a/rust-toolchain b/rust-toolchain index ee2d639b..8ca4b5ed 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2023-08-12 \ No newline at end of file +nightly-2024-02-03