add hash of public params for Nova & HyperNova (#118)

- implement hash of public params for Nova & HyperNova
- abstract pp_hash computation for folding schemes
- add pp_hash to solidity contract generator to verify the decider proof
This commit is contained in:
2024-07-05 11:47:18 +02:00
committed by GitHub
parent b5667968f4
commit c17fcf56c6
33 changed files with 665 additions and 406 deletions

View File

@@ -3,7 +3,7 @@ use crate::utils::encoding::{G1Repr, G2Repr};
use crate::utils::HeaderInclusion;
use crate::{ProtocolVerifierKey, GPL3_SDPX_IDENTIFIER};
use ark_bn254::Bn254;
use ark_groth16::VerifyingKey;
use ark_groth16::VerifyingKey as ArkVerifyingKey;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use askama::Template;
@@ -48,10 +48,10 @@ impl From<Groth16VerifierKey> for Groth16Verifier {
// Ideally this would be linked to the `Decider` trait in FoldingSchemes.
// For now, this is the easiest as NovaCycleFold isn't clear target from where we can get all it's needed arguments.
#[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)]
pub struct Groth16VerifierKey(pub(crate) VerifyingKey<Bn254>);
pub struct Groth16VerifierKey(pub(crate) ArkVerifyingKey<Bn254>);
impl From<VerifyingKey<Bn254>> for Groth16VerifierKey {
fn from(value: VerifyingKey<Bn254>) -> Self {
impl From<ArkVerifyingKey<Bn254>> for Groth16VerifierKey {
fn from(value: ArkVerifyingKey<Bn254>) -> Self {
Self(value)
}
}
@@ -95,7 +95,7 @@ mod tests {
#[test]
fn groth16_vk_serde_roundtrip() {
let (_, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
let (_, _, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
let g16_vk = Groth16VerifierKey::from(vk);
let mut bytes = vec![];
@@ -109,7 +109,7 @@ mod tests {
#[test]
fn test_groth16_verifier_accepts_and_rejects_proofs() {
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
let (_, _, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
let (_, _, _, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
let g16_vk = Groth16VerifierKey::from(g16_vk);
let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();

View File

@@ -102,7 +102,7 @@ mod tests {
#[test]
fn kzg_vk_serde_roundtrip() {
let (pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec()));
let mut bytes = vec![];
@@ -115,7 +115,7 @@ mod tests {
#[test]
fn kzg_verifier_compiles() {
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
let res = HeaderInclusion::<KZG10Verifier>::builder()
@@ -136,7 +136,7 @@ mod tests {
let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))

View File

@@ -97,6 +97,7 @@ pub mod tests {
pub fn setup<'a>(
n: usize,
) -> (
Fr, // public params hash
KZGProverKey<'a, G1>,
KZGVerifierKey<Bn254>,
ark_groth16::ProvingKey<Bn254>,
@@ -115,6 +116,7 @@ pub mod tests {
let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, n).unwrap();
(kzg_pk, kzg_vk, g16_pk, g16_vk, circuit)
let pp_hash = Fr::from(42u32); // only for test
(pp_hash, kzg_pk, kzg_vk, g16_pk, g16_vk, circuit)
}
}

View File

@@ -1,9 +1,10 @@
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
use ark_bn254::{Bn254, Fq, G1Affine};
use ark_groth16::VerifyingKey;
use ark_poly_commit::kzg10::VerifierKey;
use ark_bn254::{Bn254, Fq, Fr, G1Affine};
use ark_groth16::VerifyingKey as ArkG16VerifierKey;
use ark_poly_commit::kzg10::VerifierKey as ArkKZG10VerifierKey;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use askama::Template;
@@ -27,6 +28,7 @@ pub fn get_decider_template_for_cyclefold_decider(
#[derive(Template, Default)]
#[template(path = "nova_cyclefold_decider.askama.sol", ext = "sol")]
pub struct NovaCycleFoldDecider {
pp_hash: Fr, // public params hash
groth16_verifier: Groth16Verifier,
kzg10_verifier: KZG10Verifier,
// z_len denotes the FCircuit state (z_i) length
@@ -42,6 +44,7 @@ impl From<NovaCycleFoldVerifierKey> for NovaCycleFoldDecider {
let public_inputs_len = groth16_verifier.gamma_abc_len;
let bits_per_limb = NonNativeUintVar::<Fq>::bits_per_limb();
Self {
pp_hash: value.pp_hash,
groth16_verifier,
kzg10_verifier: KZG10Verifier::from(value.kzg_vk),
z_len: value.z_len,
@@ -54,6 +57,7 @@ impl From<NovaCycleFoldVerifierKey> for NovaCycleFoldDecider {
#[derive(CanonicalDeserialize, CanonicalSerialize, PartialEq, Debug, Clone)]
pub struct NovaCycleFoldVerifierKey {
pp_hash: Fr,
g16_vk: Groth16VerifierKey,
kzg_vk: KZG10VerifierKey,
z_len: usize,
@@ -73,25 +77,37 @@ impl ProtocolVerifierKey for NovaCycleFoldVerifierKey {
}
}
impl From<(Groth16VerifierKey, KZG10VerifierKey, usize)> for NovaCycleFoldVerifierKey {
fn from(value: (Groth16VerifierKey, KZG10VerifierKey, usize)) -> Self {
impl From<(Fr, Groth16VerifierKey, KZG10VerifierKey, usize)> for NovaCycleFoldVerifierKey {
fn from(value: (Fr, Groth16VerifierKey, KZG10VerifierKey, usize)) -> Self {
Self {
g16_vk: value.0,
kzg_vk: value.1,
z_len: value.2,
pp_hash: value.0,
g16_vk: value.1,
kzg_vk: value.2,
z_len: value.3,
}
}
}
// implements From assuming that the 'batchCheck' method from the KZG10 template will not be used
// in the NovaCycleFoldDecider verifier contract
impl From<((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)> for NovaCycleFoldVerifierKey {
fn from(value: ((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)) -> Self {
impl
From<(
(Fr, ArkG16VerifierKey<Bn254>, ArkKZG10VerifierKey<Bn254>),
usize,
)> for NovaCycleFoldVerifierKey
{
fn from(
value: (
(Fr, ArkG16VerifierKey<Bn254>, ArkKZG10VerifierKey<Bn254>),
usize,
),
) -> Self {
let decider_vp = value.0;
let g16_vk = Groth16VerifierKey::from(decider_vp.0);
let g16_vk = Groth16VerifierKey::from(decider_vp.1);
// pass `Vec::new()` since batchCheck will not be used
let kzg_vk = KZG10VerifierKey::from((decider_vp.1, Vec::new()));
let kzg_vk = KZG10VerifierKey::from((decider_vp.2, Vec::new()));
Self {
pp_hash: decider_vp.0,
g16_vk,
kzg_vk,
z_len: value.1,
@@ -101,12 +117,14 @@ impl From<((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)> for NovaCycleFoldV
impl NovaCycleFoldVerifierKey {
pub fn new(
vkey_g16: VerifyingKey<Bn254>,
vkey_kzg: VerifierKey<Bn254>,
pp_hash: Fr,
vkey_g16: ArkG16VerifierKey<Bn254>,
vkey_kzg: ArkKZG10VerifierKey<Bn254>,
crs_points: Vec<G1Affine>,
z_len: usize,
) -> Self {
Self {
pp_hash,
g16_vk: Groth16VerifierKey::from(vkey_g16),
kzg_vk: KZG10VerifierKey::from((vkey_kzg, crs_points)),
z_len,
@@ -117,12 +135,9 @@ impl NovaCycleFoldVerifierKey {
#[cfg(test)]
mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1};
use ark_crypto_primitives::snark::SNARK;
use ark_ff::PrimeField;
use ark_groth16::VerifyingKey as G16VerifierKey;
use ark_groth16::{Groth16, ProvingKey};
use ark_groth16::Groth16;
use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::fp::FpVar;
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
@@ -132,15 +147,10 @@ mod tests {
use std::time::Instant;
use folding_schemes::{
commitment::{
kzg::{ProverKey as KZGProverKey, KZG},
pedersen::Pedersen,
CommitmentScheme,
},
commitment::{kzg::KZG, pedersen::Pedersen},
folding::nova::{
decider_eth::{prepare_calldata, Decider as DeciderEth},
decider_eth_circuit::DeciderEthCircuit,
get_cs_params_len, Nova, ProverParams,
Nova, PreprocessorParam,
},
frontend::FCircuit,
transcript::poseidon::poseidon_canonical_config,
@@ -156,6 +166,24 @@ mod tests {
NovaCycleFoldVerifierKey, ProtocolVerifierKey,
};
type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
type DECIDER<FC> = DeciderEth<
G1,
GVar,
G2,
GVar2,
FC,
KZG<'static, Bn254>,
Pedersen<G2>,
Groth16<Bn254>,
NOVA<FC>,
>;
type FS_PP<FC> = <NOVA<FC> as FoldingScheme<G1, G2, FC>>::ProverParam;
type FS_VP<FC> = <NOVA<FC> as FoldingScheme<G1, G2, FC>>::VerifierParam;
type DECIDER_PP<FC> = <DECIDER<FC> as Decider<G1, G2, FC, NOVA<FC>>>::ProverParam;
type DECIDER_VP<FC> = <DECIDER<FC> as Decider<G1, G2, FC, NOVA<FC>>>::VerifierParam;
/// Test circuit to be folded
#[derive(Clone, Copy, Debug)]
pub struct CubicFCircuit<F: PrimeField> {
@@ -256,10 +284,10 @@ mod tests {
#[test]
fn nova_cyclefold_vk_serde_roundtrip() {
let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let (pp_hash, _, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let mut bytes = vec![];
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1));
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((pp_hash, g16_vk, kzg_vk), 1));
nova_cyclefold_vk
.serialize_protocol_verifier_key(&mut bytes)
@@ -272,8 +300,8 @@ mod tests {
#[test]
fn nova_cyclefold_decider_template_renders() {
let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1));
let (pp_hash, _, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((pp_hash, g16_vk, kzg_vk), 1));
let decider_solidity_code = HeaderInclusion::<NovaCycleFoldDecider>::builder()
.template(nova_cyclefold_vk)
@@ -282,59 +310,29 @@ mod tests {
save_solidity("NovaDecider.sol", &decider_solidity_code.render().unwrap());
}
#[allow(clippy::type_complexity)]
fn init_test_prover_params<FC: FCircuit<Fr, Params = ()>>() -> (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
) {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>();
let f_circuit = FC::new(()).unwrap();
let (cs_len, cf_cs_len) =
get_cs_params_len::<G1, GVar, G2, GVar2, FC>(&poseidon_config, f_circuit).unwrap();
let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap();
let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> {
poseidon_config: poseidon_config.clone(),
cs_pp: kzg_pk.clone(),
cf_cs_pp: cf_pedersen_params,
};
(fs_prover_params, kzg_vk)
}
/// Initializes Nova parameters and DeciderEth parameters. Only for test purposes.
#[allow(clippy::type_complexity)]
fn init_params<FC: FCircuit<Fr, Params = ()>>() -> (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
ProvingKey<Bn254>,
G16VerifierKey<Bn254>,
) {
fn init_params<FC: FCircuit<Fr, Params = ()>>(
) -> ((FS_PP<FC>, FS_VP<FC>), (DECIDER_PP<FC>, DECIDER_VP<FC>)) {
let mut rng = rand::rngs::OsRng;
let start = Instant::now();
let (fs_prover_params, kzg_vk) = init_test_prover_params::<FC>();
println!("generated Nova folding params: {:?}", start.elapsed());
let poseidon_config = poseidon_canonical_config::<Fr>();
let f_circuit = FC::new(()).unwrap();
pub type NOVA_FCircuit<FC> =
Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
let z_0 = vec![Fr::zero(); f_circuit.state_len()];
let nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap();
let decider_circuit =
DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>(
nova.clone(),
)
.unwrap();
let start = Instant::now();
let (g16_pk, g16_vk) =
Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
println!(
"generated G16 (Decider circuit) params: {:?}",
start.elapsed()
let prep_param = PreprocessorParam::<G1, G2, FC, KZG<'static, Bn254>, Pedersen<G2>>::new(
poseidon_config,
f_circuit.clone(),
);
(fs_prover_params, kzg_vk, g16_pk, g16_vk)
let nova_params = NOVA::preprocess(&mut rng, &prep_param).unwrap();
let nova = NOVA::init(
nova_params.clone(),
f_circuit.clone(),
vec![Fr::zero(); f_circuit.state_len()].clone(),
)
.unwrap();
let decider_params =
DECIDER::preprocess(&mut rng, &nova_params.clone(), nova.clone()).unwrap();
(nova_params, decider_params)
}
/// This function allows to define which FCircuit to use for the test, and how many prove_step
@@ -346,52 +344,31 @@ mod tests {
/// - modifies the z_0 and checks that it does not pass the EVM check
#[allow(clippy::type_complexity)]
fn nova_cyclefold_solidity_verifier_opt<FC: FCircuit<Fr, Params = ()>>(
params: (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
ProvingKey<Bn254>,
G16VerifierKey<Bn254>,
),
fs_params: (FS_PP<FC>, FS_VP<FC>),
decider_params: (DECIDER_PP<FC>, DECIDER_VP<FC>),
z_0: Vec<Fr>,
n_steps: usize,
) {
let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = params.clone();
let (decider_pp, decider_vp) = decider_params;
pub type NOVA_FCircuit<FC> =
Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
pub type DECIDERETH_FCircuit<FC> = DeciderEth<
G1,
GVar,
G2,
GVar2,
FC,
KZG<'static, Bn254>,
Pedersen<G2>,
Groth16<Bn254>,
NOVA_FCircuit<FC>,
>;
let f_circuit = FC::new(()).unwrap();
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((
(g16_vk.clone(), kzg_vk.clone()),
f_circuit.state_len(),
));
let nova_cyclefold_vk =
NovaCycleFoldVerifierKey::from((decider_vp.clone(), f_circuit.state_len()));
let mut rng = rand::rngs::OsRng;
let mut nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0).unwrap();
let mut nova = NOVA::<FC>::init(fs_params, f_circuit, z_0).unwrap();
for _ in 0..n_steps {
nova.prove_step(&mut rng, vec![]).unwrap();
}
let start = Instant::now();
let proof =
DECIDERETH_FCircuit::prove(rng, (g16_pk, fs_prover_params.cs_pp.clone()), nova.clone())
.unwrap();
let proof = DECIDER::<FC>::prove(rng, decider_pp, nova.clone()).unwrap();
println!("generated Decider proof: {:?}", start.elapsed());
let verified = DECIDERETH_FCircuit::<FC>::verify(
(g16_vk, kzg_vk),
let verified = DECIDER::<FC>::verify(
decider_vp,
nova.i,
nova.z_0.clone(),
nova.z_i.clone(),
@@ -448,12 +425,22 @@ mod tests {
#[test]
fn nova_cyclefold_solidity_verifier() {
let params = init_params::<CubicFCircuit<Fr>>();
let (nova_params, decider_params) = init_params::<CubicFCircuit<Fr>>();
let z_0 = vec![Fr::from(3_u32)];
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(params.clone(), z_0.clone(), 2);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(params.clone(), z_0.clone(), 3);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(
nova_params.clone(),
decider_params.clone(),
z_0.clone(),
2,
);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(
nova_params,
decider_params,
z_0,
3,
);
let params = init_params::<MultiInputsFCircuit<Fr>>();
let (nova_params, decider_params) = init_params::<MultiInputsFCircuit<Fr>>();
let z_0 = vec![
Fr::from(1_u32),
Fr::from(1_u32),
@@ -462,12 +449,14 @@ mod tests {
Fr::from(1_u32),
];
nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>(
params.clone(),
nova_params.clone(),
decider_params.clone(),
z_0.clone(),
2,
);
nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>(
params.clone(),
nova_params,
decider_params,
z_0.clone(),
3,
);

View File

@@ -78,10 +78,11 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
// from gamma_abc_len, we subtract 1.
uint256[{{ public_inputs_len - 1 }}] memory public_inputs;
public_inputs[0] = i_z0_zi[0];
public_inputs[0] = {{pp_hash}};
public_inputs[1] = i_z0_zi[0];
for (uint i = 0; i < {{ z_len * 2 }}; i++) {
public_inputs[1 + i] = i_z0_zi[1 + i];
public_inputs[2 + i] = i_z0_zi[1 + i];
}
{
@@ -91,9 +92,9 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256 x0 = rlc(U_i_x_u_i_cmW[0], U_i_u_u_i_u_r[2], u_i_x_cmT[0]);
uint256 x1 = rlc(U_i_x_u_i_cmW[1], U_i_u_u_i_u_r[2], u_i_x_cmT[1]);
public_inputs[{{ z_len * 2 + 1 }}] = u;
public_inputs[{{ z_len * 2 + 2 }}] = x0;
public_inputs[{{ z_len * 2 + 3 }}] = x1;
public_inputs[{{ z_len * 2 + 2 }}] = u;
public_inputs[{{ z_len * 2 + 3 }}] = x0;
public_inputs[{{ z_len * 2 + 4 }}] = x1;
}
{
@@ -106,8 +107,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256[{{num_limbs}}] memory cmE_y_limbs = LimbsDecomposition.decompose(cmE[1]);
for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 }} + k] = cmE_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs }} + k] = cmE_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 }} + k] = cmE_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs }} + k] = cmE_y_limbs[k];
}
}
@@ -124,8 +125,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256[{{num_limbs}}] memory cmW_y_limbs = LimbsDecomposition.decompose(cmW[1]);
for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 + num_limbs * 2 }} + k] = cmW_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 3 }} + k] = cmW_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 2 }} + k] = cmW_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 3 }} + k] = cmW_y_limbs[k];
}
}
@@ -134,10 +135,10 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
{
// add challenges
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 }}] = challenge_W_challenge_E_kzg_evals[0];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 1 }}] = challenge_W_challenge_E_kzg_evals[1];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 2 }}] = challenge_W_challenge_E_kzg_evals[2];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 3 }}] = challenge_W_challenge_E_kzg_evals[3];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 }}] = challenge_W_challenge_E_kzg_evals[0];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 1 }}] = challenge_W_challenge_E_kzg_evals[1];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 2 }}] = challenge_W_challenge_E_kzg_evals[2];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 3 }}] = challenge_W_challenge_E_kzg_evals[3];
uint256[{{num_limbs}}] memory cmT_x_limbs;
uint256[{{num_limbs}}] memory cmT_y_limbs;
@@ -146,8 +147,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
cmT_y_limbs = LimbsDecomposition.decompose(u_i_x_cmT[3]);
for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 }} + 4 + k] = cmT_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 5}} + 4 + k] = cmT_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 }} + 4 + k] = cmT_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 5}} + 4 + k] = cmT_y_limbs[k];
}
// last element of the groth16 proof's public inputs is `r`