mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-11 08:21:37 +01:00
Add solidity verifier of the nova+cyclefold (#87)
* Add solidity verifier of the nova+cyclefold, and add method to prepare the calldata from Decider's proof. Missing conversion of the point coordinates into limbs (ark compatible) * chore: adding comments linking to the contract's signature * chore: update .gitignore * chore: add num-bigint as dev dependency * fix: work with abs path for storing generated sol code * chore: update comment * feat: solidity verifier working on single and multi-input circuits * feat: multi-input folding verification working + fixing encoding of additive identity in calldata * chore: make bigint a dependency * refactor: import utils functions from utils.rs and make them available from anywhere * chore: make utils and evm available publicly * fix: pub mod instead * chore: make relevant method public and add `get_decider_template_for_cyclefold_decider` to exported objects * solidity-verifiers: move tests to their corresponding files * small update: Cyclefold -> CycleFold at the missing places * abstract nova-cyclefold solidity verifiers tests to avoid code duplication, and abstract also the computed setup params (FS & Decider) to compute them only once for all related tests to save test time * small polish after rebase to last main branch changes * rm unneeded Option for KZGData::g1_crs_batch_points * add checks modifying z_0 & z_i to nova_cyclefold_solidity_verifier test * add light-test feature to decider_eth_circuit to use it in solidity-verifier tests without the big circuit * solidity-verifiers: groth16 template: port the fix from https://github.com/iden3/snarkjs/pull/480 & https://github.com/iden3/snarkjs/issues/479 * add print warning msg for light-test in DeciderEthCircuit * solidity-verifiers: update limbs logic to nonnative last version, parametrize limbs params solidity-verifiers: * update solidity limbs logic to last nonnative impl version, and to last u_i.x impl * parametrize limbs params * add light-test feature: replace the '#[cfg(not(test))]' by the 'light-test' feature that by default is not enabled, so when running the github actions we enable the feature 'light-tests', and then we can have a full-test that runs the test without the 'light-tests' flag, but we don't run this big test every time. The choice of a feature is to allow us to control this from other-crates tests (for example for the solidity-verifier separated crate tests, to avoid running the full heavy circuit in the solidity tests) * move solidity constants into template constants for auto compute of params * polishing * revm use only needed feature This is to avoid c depencency for c-kzg which is behind the c-kzg flag and not needed. * nova_cyclefold_decider.sol header * rearrange test helpers position, add error for min number of steps * in solidity-verifiers: 'data'->'vk/verifier key' * add From for NovaCycleFoldVerifierKey from original vks to simplify dev flow, also conditionally template the batchCheck related structs and methods from the KZG10 solidity template --------- Co-authored-by: dmpierre <pdaixmoreux@gmail.com>
This commit is contained in:
@@ -174,7 +174,7 @@ impl<F: PrimeField> ToBitsGadget<F> for LimbVar<F> {
|
||||
pub struct NonNativeUintVar<F: PrimeField>(pub Vec<LimbVar<F>>);
|
||||
|
||||
impl<F: PrimeField> NonNativeUintVar<F> {
|
||||
const fn bits_per_limb() -> usize {
|
||||
pub const fn bits_per_limb() -> usize {
|
||||
assert!(F::MODULUS_BIT_SIZE > 250);
|
||||
// For a `F` with order > 250 bits, 55 is chosen for optimizing the most
|
||||
// expensive part `Az∘Bz` when checking the R1CS relation for CycleFold.
|
||||
|
||||
@@ -245,7 +245,7 @@ pub mod tests {
|
||||
/// - M(01) = 4*eq_00(r) + 3*eq_10(r) + 9*eq_01(r) + 2*eq_11(r)
|
||||
/// - M(11) = 4*eq_00(r) + 2*eq_10(r) + 2*eq_01(r) + 0*eq_11(r)
|
||||
///
|
||||
/// This is used by Hypernova in LCCCS to perform a verifier-chosen random linear combination between the columns
|
||||
/// This is used by HyperNova in LCCCS to perform a verifier-chosen random linear combination between the columns
|
||||
/// of the matrix and the z vector. This technique is also used extensively in "An Algebraic Framework for
|
||||
/// Universal and Updatable SNARKs".
|
||||
#[test]
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
/// This file implements the onchain (Ethereum's EVM) decider.
|
||||
use ark_bn254::Bn254;
|
||||
use ark_crypto_primitives::sponge::Absorb;
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_groth16::Groth16;
|
||||
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
|
||||
use ark_snark::SNARK;
|
||||
use ark_std::rand::{CryptoRng, RngCore};
|
||||
use ark_std::Zero;
|
||||
use ark_std::{One, Zero};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub use super::decider_eth_circuit::{DeciderEthCircuit, KZGChallengesGadget};
|
||||
use super::{circuits::CF2, nifs::NIFS, CommittedInstance, Nova};
|
||||
use crate::commitment::{
|
||||
kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme,
|
||||
kzg::{Proof as KZGProof, KZG},
|
||||
pedersen::Params as PedersenParams,
|
||||
CommitmentScheme,
|
||||
};
|
||||
use crate::folding::circuits::nonnative::affine::NonNativeAffineVar;
|
||||
use crate::frontend::FCircuit;
|
||||
@@ -143,8 +147,12 @@ where
|
||||
z_i: Vec<C1::ScalarField>,
|
||||
running_instance: &Self::CommittedInstance,
|
||||
incoming_instance: &Self::CommittedInstance,
|
||||
proof: Self::Proof,
|
||||
proof: &Self::Proof,
|
||||
) -> Result<bool, Error> {
|
||||
if i <= C1::ScalarField::one() {
|
||||
return Err(Error::NotEnoughSteps);
|
||||
}
|
||||
|
||||
let (snark_vk, cs_vk): (S::VerifyingKey, CS1::VerifierParams) = vp;
|
||||
|
||||
// compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT)
|
||||
@@ -199,6 +207,78 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares solidity calldata for calling the NovaDecider contract
|
||||
pub fn prepare_calldata(
|
||||
function_signature_check: [u8; 4],
|
||||
i: ark_bn254::Fr,
|
||||
z_0: Vec<ark_bn254::Fr>,
|
||||
z_i: Vec<ark_bn254::Fr>,
|
||||
running_instance: &CommittedInstance<ark_bn254::G1Projective>,
|
||||
incoming_instance: &CommittedInstance<ark_bn254::G1Projective>,
|
||||
proof: Proof<ark_bn254::G1Projective, KZG<'static, Bn254>, Groth16<Bn254>>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
Ok(vec![
|
||||
function_signature_check.to_vec(),
|
||||
i.into_bigint().to_bytes_be(), // i
|
||||
z_0.iter()
|
||||
.flat_map(|v| v.into_bigint().to_bytes_be())
|
||||
.collect::<Vec<u8>>(), // z_0
|
||||
z_i.iter()
|
||||
.flat_map(|v| v.into_bigint().to_bytes_be())
|
||||
.collect::<Vec<u8>>(), // z_i
|
||||
point_to_eth_format(running_instance.cmW.into_affine())?, // U_i_cmW
|
||||
point_to_eth_format(running_instance.cmE.into_affine())?, // U_i_cmE
|
||||
running_instance.u.into_bigint().to_bytes_be(), // U_i_u
|
||||
incoming_instance.u.into_bigint().to_bytes_be(), // u_i_u
|
||||
proof.r.into_bigint().to_bytes_be(), // r
|
||||
running_instance
|
||||
.x
|
||||
.iter()
|
||||
.flat_map(|v| v.into_bigint().to_bytes_be())
|
||||
.collect::<Vec<u8>>(), // U_i_x
|
||||
point_to_eth_format(incoming_instance.cmW.into_affine())?, // u_i_cmW
|
||||
incoming_instance
|
||||
.x
|
||||
.iter()
|
||||
.flat_map(|v| v.into_bigint().to_bytes_be())
|
||||
.collect::<Vec<u8>>(), // u_i_x
|
||||
point_to_eth_format(proof.cmT.into_affine())?, // cmT
|
||||
point_to_eth_format(proof.snark_proof.a)?, // pA
|
||||
point2_to_eth_format(proof.snark_proof.b)?, // pB
|
||||
point_to_eth_format(proof.snark_proof.c)?, // pC
|
||||
proof.kzg_challenges[0].into_bigint().to_bytes_be(), // challenge_W
|
||||
proof.kzg_challenges[1].into_bigint().to_bytes_be(), // challenge_E
|
||||
proof.kzg_proofs[0].eval.into_bigint().to_bytes_be(), // eval W
|
||||
proof.kzg_proofs[1].eval.into_bigint().to_bytes_be(), // eval E
|
||||
point_to_eth_format(proof.kzg_proofs[0].proof.into_affine())?, // W kzg_proof
|
||||
point_to_eth_format(proof.kzg_proofs[1].proof.into_affine())?, // E kzg_proof
|
||||
]
|
||||
.concat())
|
||||
}
|
||||
|
||||
fn point_to_eth_format<C: AffineRepr>(p: C) -> Result<Vec<u8>, Error>
|
||||
where
|
||||
C::BaseField: PrimeField,
|
||||
{
|
||||
// the encoding of the additive identity is [0, 0] on the EVM
|
||||
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
|
||||
let (x, y) = p.xy().unwrap_or(zero_point);
|
||||
|
||||
Ok([x.into_bigint().to_bytes_be(), y.into_bigint().to_bytes_be()].concat())
|
||||
}
|
||||
fn point2_to_eth_format(p: ark_bn254::G2Affine) -> Result<Vec<u8>, Error> {
|
||||
let zero_point = (&ark_bn254::Fq2::zero(), &ark_bn254::Fq2::zero());
|
||||
let (x, y) = p.xy().unwrap_or(zero_point);
|
||||
|
||||
Ok([
|
||||
x.c1.into_bigint().to_bytes_be(),
|
||||
x.c0.into_bigint().to_bytes_be(),
|
||||
y.c1.into_bigint().to_bytes_be(),
|
||||
y.c0.into_bigint().to_bytes_be(),
|
||||
]
|
||||
.concat())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
@@ -298,7 +378,7 @@ pub mod tests {
|
||||
let start = Instant::now();
|
||||
let decider_vp = (g16_vk, kzg_vk);
|
||||
let verified = DECIDER::verify(
|
||||
decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof,
|
||||
decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(verified);
|
||||
|
||||
@@ -426,10 +426,14 @@ where
|
||||
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
||||
(u_i.x[0]).enforce_equal(&u_i_x)?;
|
||||
|
||||
#[cfg(feature = "light-test")]
|
||||
println!("[WARNING]: Running with the 'light-test' feature, skipping the big part of the DeciderEthCircuit.\n Only for testing purposes.");
|
||||
|
||||
// The following two checks (and their respective allocations) are disabled for normal
|
||||
// tests since they take several millions of constraints and would take several minutes
|
||||
// (and RAM) to run the test.
|
||||
#[cfg(not(test))]
|
||||
// (and RAM) to run the test. It is active by default, and not active only when
|
||||
// 'light-test' feature is used.
|
||||
#[cfg(not(feature = "light-test"))]
|
||||
{
|
||||
// imports here instead of at the top of the file, so we avoid having multiple
|
||||
// `#[cfg(not(test))]`
|
||||
|
||||
@@ -307,7 +307,7 @@ fn pow_i<F: PrimeField>(i: usize, betas: &Vec<F>) -> F {
|
||||
|
||||
/// calculates F[x] using the optimized binary-tree technique
|
||||
/// described in Claim 4.4
|
||||
/// of [Protogalaxy](https://eprint.iacr.org/2023/1106.pdf)
|
||||
/// of [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf)
|
||||
fn calc_f_from_btree<F: PrimeField>(
|
||||
fw: &[F],
|
||||
betas: &[F],
|
||||
|
||||
Reference in New Issue
Block a user