mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-11 08:21:37 +01:00
Add CLI interface for verifier contract generation (#74)
* add: solidity-verifier workspace member * chore: Update toolchain to 1.74 * feat: Add basic clap cli interface for solidity verifier This includes a cli parser that serves as a way to the user to generate the desired Solidity contracts. * chore: Expose SoldityVerifier template struct * feat: Finish first working version * change: Modify some settings * fix: Fix rebase conflicts * chore: Leave resolver 2 for workspace * chore: Rename KZG+G16 template Now the template refers to Nova + Cyclefold and has a Warning attached to it * fixup * chore: Rename to NovaCyclefoldDecider the template * chore: Change constructors to `new` instead of `from` * add: ProtocolData trait helper This trait helps to treat the serialized data required by the Template as a single element while still allowing a flexible usage. This is specially interesting as allows the cli to operate considering a single path of input data where all the data for the selected protocol co-exists. Reducing the amount of parsing and arguments the user needs to pass to the cli. * chore: Create `From` impls formally Previously we had functions called `from` which had nothing to do with the trait `From`. This addresses this issue and fixes it. Now both `new` and `from` are avaliable. But `from` follows the `From` trait. * add: Support G16, KZG and Nova+Cyclefold in cli This adds a `render` fn for `Protocol` which makes it easier to add new protocols to the CLI as is mainly based in the `ProtocolData` impl behind the scenes of the selected protocol. Aside from that, this commit reworks some minor parts of the CLI config as shorteners for commands or adding `pragma` as an optional parameter. * chore: Adapt `main.rs` to new cli changes As seen, this allows to have a much easier `main.rs` which doesn't have to do any `match` over the selected protocol. * chore: Make solidity helper fns `cfg(test)` * chore: Rework folding-schemes-solidity structure * chore: Remove g1_crs_batch_points_len from KZGData * add: Serde tests for all template targets * tmp: Add NovaCyclefold testing * add: HeaderInclusion template When we use templates that are composed by others (as happens with `NovaCyclefold` one) we sadly see that the License and the `pragma` attributes are rendered once per sub-template. This generic structure solves this issue by being actually the only item rendered which has a sub-template the template we indeed want to render at the end. * chore: Add tests for NovaCyclefold contract This also includes small changes to the lib architecture such as adding constants for GPL3_SDPX_IDENTIFIER or move the default pragma versions used to `mod.rs` * chore: Update g16 to use HeaderInclusion template rendering Now the `ProtocolData` impl falls back to the usage of `HeaderInclusion` it is easier to handle complex templates like `NovaCyclefold`. * add: Small builder-pattern to construct HeaderInclusion Templates As mentioned in previous commits, the idea is that the header is set on an automatic wrapper template applied to the one that we actually want to render. This builder pattern makes it less complex to do such a thing. Specially avoiding unidiomatic `From` implementations. * remove: sdpx & pragma from KZG template Those are externalized and handled by HeaderInclusion template utility * chore: Update templates to use HeaderInclusion builder * chore: Update tests to use HeaderInclusion builderPattern * remove: fixed pragma version in novacyclefold template * chore: Accept Into<Template> in builder * tmp: Only KZG return passes. Fix Groth * fix: Prevent `revert` from paniking for negative tests * feat: Merge G16 and KZG contract results in NovaCyclefold * chore: Add assets for quicker/easier testing Now instead of generating the protocoldata & proofs on each test, we just deserialize * fix: Address clippy & warnings * fix: Spelling to prevent PR farmers LOL * chore: Add about and long_about to CLI tool * add: README.md * chore: Revert asset-based testing approach * remove: Assets folder * fix: Rebase issues * fix: use &mut for Reader * fix: rebase error with Contract name * chore: Reduce tests LOC with setup fn * chore: Set MIT license indentifier for CLI & KZG * chore: Add extra usage example * chore: Update novacyclefold contract comments on soundess * chore: Typo * chore: Allow type complexity clippy for setup fn * chore: Address Pierre's comments * chore: Rename workspace members - folding-schemes-solidity -> soliity-verifiers
This commit is contained in:
166
solidity-verifiers/src/evm.rs
Normal file
166
solidity-verifiers/src/evm.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
pub use revm;
|
||||
use revm::{
|
||||
primitives::{hex, Address, CreateScheme, ExecutionResult, Output, TransactTo, TxEnv},
|
||||
InMemoryDB, EVM,
|
||||
};
|
||||
use std::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
fs::{create_dir_all, File},
|
||||
io::{self, Write},
|
||||
process::{Command, Stdio},
|
||||
str,
|
||||
};
|
||||
|
||||
// from: https://github.com/privacy-scaling-explorations/halo2-solidity-verifier/blob/85cb77b171ce3ee493628007c7a1cfae2ea878e6/examples/separately.rs#L56
|
||||
pub(crate) fn save_solidity(name: impl AsRef<str>, solidity: &str) {
|
||||
const DIR_GENERATED: &str = "./generated";
|
||||
create_dir_all(DIR_GENERATED).unwrap();
|
||||
File::create(format!("{DIR_GENERATED}/{}", name.as_ref()))
|
||||
.unwrap()
|
||||
.write_all(solidity.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Compile solidity with `--via-ir` flag, then return creation bytecode.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if executable `solc` can not be found, or compilation fails.
|
||||
pub(crate) fn compile_solidity(solidity: impl AsRef<[u8]>, contract_name: &str) -> Vec<u8> {
|
||||
let mut process = match Command::new("solc")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.arg("--bin")
|
||||
.arg("--optimize")
|
||||
.arg("-")
|
||||
.spawn()
|
||||
{
|
||||
Ok(process) => process,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
panic!("Command 'solc' not found");
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to spawn process with command 'solc':\n{err}");
|
||||
}
|
||||
};
|
||||
process
|
||||
.stdin
|
||||
.take()
|
||||
.unwrap()
|
||||
.write_all(solidity.as_ref())
|
||||
.unwrap();
|
||||
let output = process.wait_with_output().unwrap();
|
||||
let stdout = str::from_utf8(&output.stdout).unwrap();
|
||||
if let Some(binary) = find_binary(stdout, contract_name) {
|
||||
binary
|
||||
} else {
|
||||
panic!(
|
||||
"Compilation fails:\n{}",
|
||||
str::from_utf8(&output.stderr).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find binary from `stdout` with given `contract_name`.
|
||||
/// `contract_name` is provided since `solc` may compile multiple contracts or libraries.
|
||||
/// hence, we need to find the correct binary.
|
||||
fn find_binary(stdout: &str, contract_name: &str) -> Option<Vec<u8>> {
|
||||
let start_contract = stdout.find(contract_name)?;
|
||||
let stdout_contract = &stdout[start_contract..];
|
||||
let start = stdout_contract.find("Binary:")? + 8;
|
||||
Some(hex::decode(&stdout_contract[start..stdout_contract.len() - 1]).unwrap())
|
||||
}
|
||||
|
||||
/// Evm runner.
|
||||
pub(crate) struct Evm {
|
||||
evm: EVM<InMemoryDB>,
|
||||
}
|
||||
|
||||
impl Debug for Evm {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let mut debug_struct = f.debug_struct("Evm");
|
||||
debug_struct
|
||||
.field("env", &self.evm.env)
|
||||
.field("db", &self.evm.db.as_ref().unwrap())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Evm {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
evm: EVM {
|
||||
env: Default::default(),
|
||||
db: Some(Default::default()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Evm {
|
||||
/// Apply create transaction with given `bytecode` as creation bytecode.
|
||||
/// Return created `address`.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if execution reverts or halts unexpectedly.
|
||||
pub(crate) fn create(&mut self, bytecode: Vec<u8>) -> Address {
|
||||
let (_, output) = self.transact_success_or_panic(TxEnv {
|
||||
gas_limit: u64::MAX,
|
||||
transact_to: TransactTo::Create(CreateScheme::Create),
|
||||
data: bytecode.into(),
|
||||
..Default::default()
|
||||
});
|
||||
match output {
|
||||
Output::Create(_, Some(address)) => address,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply call transaction to given `address` with `calldata`.
|
||||
/// Returns `gas_used` and `return_data`.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if execution reverts or halts unexpectedly.
|
||||
pub(crate) fn call(&mut self, address: Address, calldata: Vec<u8>) -> (u64, Vec<u8>) {
|
||||
let (gas_used, output) = self.transact_success_or_panic(TxEnv {
|
||||
gas_limit: u64::MAX,
|
||||
transact_to: TransactTo::Call(address),
|
||||
data: calldata.into(),
|
||||
..Default::default()
|
||||
});
|
||||
match output {
|
||||
Output::Call(output) => (gas_used, output.into()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn transact_success_or_panic(&mut self, tx: TxEnv) -> (u64, Output) {
|
||||
self.evm.env.tx = tx;
|
||||
let result = self.evm.transact_commit().unwrap();
|
||||
self.evm.env.tx = Default::default();
|
||||
match result {
|
||||
ExecutionResult::Success {
|
||||
gas_used,
|
||||
output,
|
||||
logs,
|
||||
..
|
||||
} => {
|
||||
if !logs.is_empty() {
|
||||
println!("--- logs from {} ---", logs[0].address);
|
||||
for (log_idx, log) in logs.iter().enumerate() {
|
||||
println!("log#{log_idx}");
|
||||
for (topic_idx, topic) in log.topics.iter().enumerate() {
|
||||
println!(" topic{topic_idx}: {topic:?}");
|
||||
}
|
||||
}
|
||||
println!("--- end ---");
|
||||
}
|
||||
(gas_used, output)
|
||||
}
|
||||
ExecutionResult::Revert { gas_used, output } => (gas_used, Output::Call(output)),
|
||||
ExecutionResult::Halt { reason, gas_used } => panic!(
|
||||
"Transaction halts unexpectedly with gas_used {gas_used} and reason {reason:?}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
7
solidity-verifiers/src/lib.rs
Normal file
7
solidity-verifiers/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod evm;
|
||||
mod utils;
|
||||
mod verifiers;
|
||||
|
||||
pub use verifiers::*;
|
||||
pub use verifiers::{Groth16Data, KzgData, NovaCyclefoldData, ProtocolData};
|
||||
43
solidity-verifiers/src/utils/encoding.rs
Normal file
43
solidity-verifiers/src/utils/encoding.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
/// Defines encodings of G1 and G2 elements for use in Solidity templates.
|
||||
use ark_bn254::{Fq, G1Affine, G2Affine};
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FqWrapper(pub Fq);
|
||||
|
||||
impl Display for FqWrapper {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct G1Repr(pub [FqWrapper; 2]);
|
||||
|
||||
impl Display for G1Repr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:#?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a G1 element to a representation that can be used in Solidity templates.
|
||||
pub fn g1_to_fq_repr(g1: G1Affine) -> G1Repr {
|
||||
G1Repr([FqWrapper(g1.x), FqWrapper(g1.y)])
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct G2Repr(pub [[FqWrapper; 2]; 2]);
|
||||
|
||||
impl Display for G2Repr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:#?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a G2 element to a representation that can be used in Solidity templates.
|
||||
pub fn g2_to_fq_repr(g2: G2Affine) -> G2Repr {
|
||||
G2Repr([
|
||||
[FqWrapper(g2.x.c0), FqWrapper(g2.x.c1)],
|
||||
[FqWrapper(g2.y.c0), FqWrapper(g2.y.c1)],
|
||||
])
|
||||
}
|
||||
66
solidity-verifiers/src/utils/mod.rs
Normal file
66
solidity-verifiers/src/utils/mod.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use crate::{GPL3_SDPX_IDENTIFIER, PRAGMA_GROTH16_VERIFIER};
|
||||
use askama::Template;
|
||||
|
||||
pub mod encoding;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "header_template.askama.sol", ext = "sol")]
|
||||
pub(crate) struct HeaderInclusion<T: Template> {
|
||||
/// SPDX-License-Identifier
|
||||
pub sdpx: String,
|
||||
/// The `pragma` statement.
|
||||
pub pragma_version: String,
|
||||
/// The template to render alongside the header.
|
||||
pub template: T,
|
||||
}
|
||||
|
||||
impl<T: Template + Default> HeaderInclusion<T> {
|
||||
pub fn builder() -> HeaderInclusionBuilder<T> {
|
||||
HeaderInclusionBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeaderInclusionBuilder<T: Template + Default> {
|
||||
/// SPDX-License-Identifier
|
||||
sdpx: String,
|
||||
/// The `pragma` statement.
|
||||
pragma_version: String,
|
||||
/// The template to render alongside the header.
|
||||
template: T,
|
||||
}
|
||||
|
||||
impl<T: Template + Default> Default for HeaderInclusionBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sdpx: GPL3_SDPX_IDENTIFIER.to_string(),
|
||||
pragma_version: PRAGMA_GROTH16_VERIFIER.to_string(),
|
||||
template: T::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Template + Default> HeaderInclusionBuilder<T> {
|
||||
pub fn sdpx<S: Into<String>>(mut self, sdpx: S) -> Self {
|
||||
self.sdpx = sdpx.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pragma_version<S: Into<String>>(mut self, pragma_version: S) -> Self {
|
||||
self.pragma_version = pragma_version.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn template(mut self, template: impl Into<T>) -> Self {
|
||||
self.template = template.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> HeaderInclusion<T> {
|
||||
HeaderInclusion {
|
||||
sdpx: self.sdpx,
|
||||
pragma_version: self.pragma_version,
|
||||
template: self.template,
|
||||
}
|
||||
}
|
||||
}
|
||||
72
solidity-verifiers/src/verifiers/g16.rs
Normal file
72
solidity-verifiers/src/verifiers/g16.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use crate::utils::encoding::{g1_to_fq_repr, g2_to_fq_repr};
|
||||
use crate::utils::encoding::{G1Repr, G2Repr};
|
||||
use crate::utils::HeaderInclusion;
|
||||
use crate::{ProtocolData, GPL3_SDPX_IDENTIFIER};
|
||||
use ark_bn254::Bn254;
|
||||
use ark_groth16::VerifyingKey;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use askama::Template;
|
||||
|
||||
use super::PRAGMA_GROTH16_VERIFIER;
|
||||
|
||||
#[derive(Template, Default)]
|
||||
#[template(path = "groth16_verifier.askama.sol", ext = "sol")]
|
||||
pub(crate) struct Groth16Verifier {
|
||||
/// The `alpha * G`, where `G` is the generator of `G1`.
|
||||
pub(crate) vkey_alpha_g1: G1Repr,
|
||||
/// The `alpha * H`, where `H` is the generator of `G2`.
|
||||
pub(crate) vkey_beta_g2: G2Repr,
|
||||
/// The `gamma * H`, where `H` is the generator of `G2`.
|
||||
pub(crate) vkey_gamma_g2: G2Repr,
|
||||
/// The `delta * H`, where `H` is the generator of `G2`.
|
||||
pub(crate) vkey_delta_g2: G2Repr,
|
||||
/// Length of the `gamma_abc_g1` vector.
|
||||
pub(crate) gamma_abc_len: usize,
|
||||
/// The `gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * H`, where `H` is the generator of `E::G1`.
|
||||
pub(crate) gamma_abc_g1: Vec<G1Repr>,
|
||||
}
|
||||
|
||||
impl From<Groth16Data> for Groth16Verifier {
|
||||
fn from(g16_data: Groth16Data) -> Self {
|
||||
Self {
|
||||
vkey_alpha_g1: g1_to_fq_repr(g16_data.0.alpha_g1),
|
||||
vkey_beta_g2: g2_to_fq_repr(g16_data.0.beta_g2),
|
||||
vkey_gamma_g2: g2_to_fq_repr(g16_data.0.gamma_g2),
|
||||
vkey_delta_g2: g2_to_fq_repr(g16_data.0.delta_g2),
|
||||
gamma_abc_len: g16_data.0.gamma_abc_g1.len(),
|
||||
gamma_abc_g1: g16_data
|
||||
.0
|
||||
.gamma_abc_g1
|
||||
.iter()
|
||||
.copied()
|
||||
.map(g1_to_fq_repr)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ideally I would like to link this 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 Groth16Data(pub(crate) VerifyingKey<Bn254>);
|
||||
|
||||
impl From<VerifyingKey<Bn254>> for Groth16Data {
|
||||
fn from(value: VerifyingKey<Bn254>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtocolData for Groth16Data {
|
||||
const PROTOCOL_NAME: &'static str = "Groth16";
|
||||
|
||||
fn render_as_template(self, pragma: Option<String>) -> Vec<u8> {
|
||||
HeaderInclusion::<Groth16Verifier>::builder()
|
||||
.sdpx(GPL3_SDPX_IDENTIFIER.to_string())
|
||||
.pragma_version(pragma.unwrap_or(PRAGMA_GROTH16_VERIFIER.to_string()))
|
||||
.template(self)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap()
|
||||
.into_bytes()
|
||||
}
|
||||
}
|
||||
72
solidity-verifiers/src/verifiers/kzg.rs
Normal file
72
solidity-verifiers/src/verifiers/kzg.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use crate::utils::encoding::{g1_to_fq_repr, g2_to_fq_repr};
|
||||
use crate::utils::encoding::{G1Repr, G2Repr};
|
||||
use crate::utils::HeaderInclusion;
|
||||
use crate::{ProtocolData, MIT_SDPX_IDENTIFIER};
|
||||
use ark_bn254::{Bn254, G1Affine};
|
||||
use ark_poly_commit::kzg10::VerifierKey;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use askama::Template;
|
||||
|
||||
use super::PRAGMA_KZG10_VERIFIER;
|
||||
|
||||
#[derive(Template, Default)]
|
||||
#[template(path = "kzg10_verifier.askama.sol", ext = "sol")]
|
||||
pub(crate) struct KZG10Verifier {
|
||||
/// The generator of `G1`.
|
||||
pub(crate) g1: G1Repr,
|
||||
/// The generator of `G2`.
|
||||
pub(crate) g2: G2Repr,
|
||||
/// The verification key
|
||||
pub(crate) vk: G2Repr,
|
||||
/// Length of the trusted setup vector.
|
||||
pub(crate) g1_crs_len: usize,
|
||||
/// The trusted setup vector.
|
||||
pub(crate) g1_crs: Vec<G1Repr>,
|
||||
}
|
||||
|
||||
impl From<KzgData> for KZG10Verifier {
|
||||
fn from(data: KzgData) -> Self {
|
||||
let g1_crs_batch_points = data.g1_crs_batch_points.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
g1: g1_to_fq_repr(data.vk.g),
|
||||
g2: g2_to_fq_repr(data.vk.h),
|
||||
vk: g2_to_fq_repr(data.vk.beta_h),
|
||||
g1_crs_len: g1_crs_batch_points.len(),
|
||||
g1_crs: g1_crs_batch_points
|
||||
.iter()
|
||||
.map(|g1| g1_to_fq_repr(*g1))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)]
|
||||
pub struct KzgData {
|
||||
pub(crate) vk: VerifierKey<Bn254>,
|
||||
pub(crate) g1_crs_batch_points: Option<Vec<G1Affine>>,
|
||||
}
|
||||
|
||||
impl From<(VerifierKey<Bn254>, Option<Vec<G1Affine>>)> for KzgData {
|
||||
fn from(value: (VerifierKey<Bn254>, Option<Vec<G1Affine>>)) -> Self {
|
||||
Self {
|
||||
vk: value.0,
|
||||
g1_crs_batch_points: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtocolData for KzgData {
|
||||
const PROTOCOL_NAME: &'static str = "KZG";
|
||||
|
||||
fn render_as_template(self, pragma: Option<String>) -> Vec<u8> {
|
||||
HeaderInclusion::<KZG10Verifier>::builder()
|
||||
.sdpx(MIT_SDPX_IDENTIFIER.to_string())
|
||||
.pragma_version(pragma.unwrap_or(PRAGMA_KZG10_VERIFIER.to_string()))
|
||||
.template(self)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap()
|
||||
.into_bytes()
|
||||
}
|
||||
}
|
||||
428
solidity-verifiers/src/verifiers/mod.rs
Normal file
428
solidity-verifiers/src/verifiers/mod.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
//! Solidity templates for the verifier contracts.
|
||||
//! We use askama for templating and define which variables are required for each template.
|
||||
|
||||
// Pragma statements for verifiers
|
||||
pub(crate) const PRAGMA_GROTH16_VERIFIER: &str = "pragma solidity >=0.7.0 <0.9.0;"; // from snarkjs, avoid changing
|
||||
pub(crate) const PRAGMA_KZG10_VERIFIER: &str = "pragma solidity >=0.8.1 <=0.8.4;";
|
||||
|
||||
/// Default SDPX License identifier
|
||||
pub(crate) const GPL3_SDPX_IDENTIFIER: &str = "// SPDX-License-Identifier: GPL-3.0";
|
||||
pub(crate) const MIT_SDPX_IDENTIFIER: &str = "// SPDX-License-Identifier: MIT";
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
||||
|
||||
mod g16;
|
||||
mod kzg;
|
||||
mod nova_cyclefold;
|
||||
|
||||
pub use g16::Groth16Data;
|
||||
pub use kzg::KzgData;
|
||||
pub use nova_cyclefold::NovaCyclefoldData;
|
||||
|
||||
pub trait ProtocolData: CanonicalDeserialize + CanonicalSerialize {
|
||||
const PROTOCOL_NAME: &'static str;
|
||||
|
||||
fn serialize_name<W: Write>(&self, writer: &mut W) -> Result<(), SerializationError> {
|
||||
Self::PROTOCOL_NAME
|
||||
.to_string()
|
||||
.serialize_uncompressed(writer)
|
||||
}
|
||||
|
||||
fn serialize_protocol_data<W: Write>(&self, writer: &mut W) -> Result<(), SerializationError> {
|
||||
self.serialize_name(writer)?;
|
||||
self.serialize_compressed(writer)
|
||||
}
|
||||
fn deserialize_protocol_data<R: Read + Copy>(
|
||||
mut reader: R,
|
||||
) -> Result<Self, SerializationError> {
|
||||
let name: String = String::deserialize_uncompressed(&mut reader)?;
|
||||
let data = Self::deserialize_compressed(&mut reader)?;
|
||||
|
||||
if name != Self::PROTOCOL_NAME {
|
||||
return Err(SerializationError::InvalidData);
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn render_as_template(self, pragma: Option<String>) -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::evm::{compile_solidity, save_solidity, Evm};
|
||||
use crate::utils::HeaderInclusion;
|
||||
use crate::{Groth16Data, KzgData, NovaCyclefoldData, ProtocolData};
|
||||
use ark_bn254::{Bn254, Fr, G1Projective as G1};
|
||||
use ark_crypto_primitives::snark::{CircuitSpecificSetupSNARK, SNARK};
|
||||
use ark_ec::{AffineRepr, CurveGroup};
|
||||
use ark_ff::{BigInt, BigInteger, PrimeField};
|
||||
use ark_groth16::Groth16;
|
||||
use ark_poly_commit::kzg10::VerifierKey;
|
||||
use ark_r1cs_std::alloc::AllocVar;
|
||||
use ark_r1cs_std::eq::EqGadget;
|
||||
use ark_r1cs_std::fields::fp::FpVar;
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
|
||||
use ark_std::rand::{RngCore, SeedableRng};
|
||||
use ark_std::Zero;
|
||||
use ark_std::{test_rng, UniformRand};
|
||||
use askama::Template;
|
||||
use folding_schemes::{
|
||||
commitment::{
|
||||
kzg::{KZGProver, KZGSetup, ProverKey},
|
||||
CommitmentProver,
|
||||
},
|
||||
transcript::{
|
||||
poseidon::{poseidon_test_config, PoseidonTranscript},
|
||||
Transcript,
|
||||
},
|
||||
};
|
||||
use itertools::chain;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::g16::Groth16Verifier;
|
||||
use super::kzg::KZG10Verifier;
|
||||
use super::nova_cyclefold::NovaCyclefoldDecider;
|
||||
|
||||
// Function signatures for proof verification on kzg10 and groth16 contracts
|
||||
pub const FUNCTION_SIGNATURE_KZG10_CHECK: [u8; 4] = [0x9e, 0x78, 0xcc, 0xf7];
|
||||
pub const FUNCTION_SIGNATURE_GROTH16_VERIFY_PROOF: [u8; 4] = [0x43, 0x75, 0x3b, 0x4d];
|
||||
pub const FUNCTION_SIGNATURE_NOVA_CYCLEFOLD_CHECK: [u8; 4] = [0x37, 0x98, 0x0b, 0xb6];
|
||||
|
||||
/// Default setup length for testing.
|
||||
const DEFAULT_SETUP_LEN: usize = 5;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct TestAddCircuit<F: PrimeField> {
|
||||
_f: PhantomData<F>,
|
||||
pub x: u8,
|
||||
pub y: u8,
|
||||
pub z: u8,
|
||||
}
|
||||
|
||||
impl<F: PrimeField> ConstraintSynthesizer<F> for TestAddCircuit<F> {
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
||||
let x = FpVar::<F>::new_witness(cs.clone(), || Ok(F::from(self.x)))?;
|
||||
let y = FpVar::<F>::new_witness(cs.clone(), || Ok(F::from(self.y)))?;
|
||||
let z = FpVar::<F>::new_input(cs.clone(), || Ok(F::from(self.z)))?;
|
||||
let comp_z = x.clone() + y.clone();
|
||||
comp_z.enforce_equal(&z)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn setup<'a>(
|
||||
n: usize,
|
||||
) -> (
|
||||
ProverKey<'a, G1>,
|
||||
VerifierKey<Bn254>,
|
||||
ark_groth16::ProvingKey<Bn254>,
|
||||
ark_groth16::VerifyingKey<Bn254>,
|
||||
TestAddCircuit<Fr>,
|
||||
) {
|
||||
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
|
||||
let (x, y, z) = (21, 21, 42);
|
||||
let circuit = TestAddCircuit::<Fr> {
|
||||
_f: PhantomData,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
};
|
||||
let (g16_pk, g16_vk) = Groth16::<Bn254>::setup(circuit, &mut rng).unwrap();
|
||||
|
||||
let (kzg_pk, kzg_vk): (ProverKey<G1>, VerifierKey<Bn254>) =
|
||||
KZGSetup::<Bn254>::setup(&mut rng, n);
|
||||
(kzg_pk, kzg_vk, g16_pk, g16_vk, circuit)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn groth16_data_serde_roundtrip() {
|
||||
let (_, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
|
||||
|
||||
let g16_data = Groth16Data::from(vk);
|
||||
let mut bytes = vec![];
|
||||
g16_data.serialize_protocol_data(&mut bytes).unwrap();
|
||||
let obtained_g16_data = Groth16Data::deserialize_protocol_data(bytes.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(g16_data, obtained_g16_data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kzg_data_serde_roundtrip() {
|
||||
let (pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
|
||||
|
||||
let kzg_data = KzgData::from((vk, Some(pk.powers_of_g[0..3].to_vec())));
|
||||
let mut bytes = vec![];
|
||||
kzg_data.serialize_protocol_data(&mut bytes).unwrap();
|
||||
let obtained_kzg_data = KzgData::deserialize_protocol_data(bytes.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(kzg_data, obtained_kzg_data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nova_cyclefold_data_serde_roundtrip() {
|
||||
let (kzg_pk, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let g16_data = Groth16Data::from(g16_vk);
|
||||
let kzg_data = KzgData::from((kzg_vk, Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
|
||||
let mut bytes = vec![];
|
||||
let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data));
|
||||
|
||||
nova_cyclefold_data
|
||||
.serialize_protocol_data(&mut bytes)
|
||||
.unwrap();
|
||||
let obtained_nova_cyclefold_data =
|
||||
NovaCyclefoldData::deserialize_protocol_data(bytes.as_slice()).unwrap();
|
||||
|
||||
assert_eq!(nova_cyclefold_data, obtained_nova_cyclefold_data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nova_cyclefold_decider_template_renders() {
|
||||
let (kzg_pk, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let g16_data = Groth16Data::from(g16_vk);
|
||||
let kzg_data = KzgData::from((kzg_vk, Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data));
|
||||
|
||||
let decider_template = HeaderInclusion::<NovaCyclefoldDecider>::builder()
|
||||
.template(nova_cyclefold_data)
|
||||
.build();
|
||||
|
||||
save_solidity("NovaDecider.sol", &decider_template.render().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nova_cyclefold_decider_template_compiles() {
|
||||
let (kzg_pk, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let g16_data = Groth16Data::from(g16_vk);
|
||||
let kzg_data = KzgData::from((kzg_vk, Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data));
|
||||
|
||||
let decider_template = HeaderInclusion::<NovaCyclefoldDecider>::builder()
|
||||
.template(nova_cyclefold_data)
|
||||
.build();
|
||||
let decider_verifier_bytecode =
|
||||
compile_solidity(decider_template.render().unwrap(), "NovaDecider");
|
||||
let mut evm = Evm::default();
|
||||
_ = evm.create(decider_verifier_bytecode);
|
||||
}
|
||||
|
||||
#[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_data = Groth16Data::from(g16_vk);
|
||||
|
||||
let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();
|
||||
let res = Groth16Verifier::from(g16_data).render().unwrap();
|
||||
save_solidity("groth16_verifier.sol", &res);
|
||||
let groth16_verifier_bytecode = compile_solidity(&res, "Verifier");
|
||||
let mut evm = Evm::default();
|
||||
let verifier_address = evm.create(groth16_verifier_bytecode);
|
||||
let (a_x, a_y) = proof.a.xy().unwrap();
|
||||
let (b_x, b_y) = proof.b.xy().unwrap();
|
||||
let (c_x, c_y) = proof.c.xy().unwrap();
|
||||
let mut calldata: Vec<u8> = chain![
|
||||
FUNCTION_SIGNATURE_GROTH16_VERIFY_PROOF,
|
||||
a_x.into_bigint().to_bytes_be(),
|
||||
a_y.into_bigint().to_bytes_be(),
|
||||
b_x.c1.into_bigint().to_bytes_be(),
|
||||
b_x.c0.into_bigint().to_bytes_be(),
|
||||
b_y.c1.into_bigint().to_bytes_be(),
|
||||
b_y.c0.into_bigint().to_bytes_be(),
|
||||
c_x.into_bigint().to_bytes_be(),
|
||||
c_y.into_bigint().to_bytes_be(),
|
||||
BigInt::from(Fr::from(circuit.z)).to_bytes_be(),
|
||||
]
|
||||
.collect();
|
||||
let (_, output) = evm.call(verifier_address, calldata.clone());
|
||||
assert_eq!(*output.last().unwrap(), 1);
|
||||
|
||||
// change calldata to make it invalid
|
||||
let last_calldata_element = calldata.last_mut().unwrap();
|
||||
*last_calldata_element = 0;
|
||||
let (_, output) = evm.call(verifier_address, calldata);
|
||||
assert_eq!(*output.last().unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kzg_verifier_template_renders() {
|
||||
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let kzg_data = KzgData::from((kzg_vk.clone(), Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
|
||||
let res = HeaderInclusion::<KZG10Verifier>::builder()
|
||||
.template(kzg_data)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
// TODO: Unsure what this is testing. If we want to test correct rendering,
|
||||
// we should first check that it COMPLETELY renders to what we expect.
|
||||
assert!(res.contains(&kzg_vk.g.x.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kzg_verifier_compiles() {
|
||||
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let kzg_data = KzgData::from((kzg_vk.clone(), Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
|
||||
let res = HeaderInclusion::<KZG10Verifier>::builder()
|
||||
.template(kzg_data)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
|
||||
let mut evm = Evm::default();
|
||||
_ = evm.create(kzg_verifier_bytecode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kzg_verifier_accepts_and_rejects_proofs() {
|
||||
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
|
||||
let poseidon_config = poseidon_test_config::<Fr>();
|
||||
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_data = KzgData::from((kzg_vk.clone(), Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
|
||||
let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||
.take(DEFAULT_SETUP_LEN)
|
||||
.collect();
|
||||
let cm = KZGProver::<G1>::commit(&kzg_pk, &v, &Fr::zero()).unwrap();
|
||||
let (eval, proof) =
|
||||
KZGProver::<G1>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap();
|
||||
let template = HeaderInclusion::<KZG10Verifier>::builder()
|
||||
.template(kzg_data)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
let kzg_verifier_bytecode = compile_solidity(template, "KZG10");
|
||||
let mut evm = Evm::default();
|
||||
let verifier_address = evm.create(kzg_verifier_bytecode);
|
||||
|
||||
let (cm_affine, proof_affine) = (cm.into_affine(), proof.into_affine());
|
||||
let (x_comm, y_comm) = cm_affine.xy().unwrap();
|
||||
let (x_proof, y_proof) = proof_affine.xy().unwrap();
|
||||
let y = eval.into_bigint().to_bytes_be();
|
||||
|
||||
transcript_v.absorb_point(&cm).unwrap();
|
||||
let x = transcript_v.get_challenge();
|
||||
|
||||
let x = x.into_bigint().to_bytes_be();
|
||||
let mut calldata: Vec<u8> = chain![
|
||||
FUNCTION_SIGNATURE_KZG10_CHECK,
|
||||
x_comm.into_bigint().to_bytes_be(),
|
||||
y_comm.into_bigint().to_bytes_be(),
|
||||
x_proof.into_bigint().to_bytes_be(),
|
||||
y_proof.into_bigint().to_bytes_be(),
|
||||
x.clone(),
|
||||
y,
|
||||
]
|
||||
.collect();
|
||||
|
||||
let (_, output) = evm.call(verifier_address, calldata.clone());
|
||||
assert_eq!(*output.last().unwrap(), 1);
|
||||
|
||||
// change calldata to make it invalid
|
||||
let last_calldata_element = calldata.last_mut().unwrap();
|
||||
*last_calldata_element = 0;
|
||||
let (_, output) = evm.call(verifier_address, calldata);
|
||||
assert_eq!(*output.last().unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nova_cyclefold_verifier_compiles() {
|
||||
let (kzg_pk, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
|
||||
let g16_data = Groth16Data::from(g16_vk);
|
||||
let kzg_data = KzgData::from((kzg_vk, Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data));
|
||||
|
||||
let decider_template = HeaderInclusion::<NovaCyclefoldDecider>::builder()
|
||||
.template(nova_cyclefold_data)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
let nova_cyclefold_verifier_bytecode = compile_solidity(decider_template, "NovaDecider");
|
||||
let mut evm = Evm::default();
|
||||
_ = evm.create(nova_cyclefold_verifier_bytecode);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nova_cyclefold_verifier_accepts_and_rejects_proofs() {
|
||||
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
|
||||
let (kzg_pk, kzg_vk, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
|
||||
let g16_data = Groth16Data::from(g16_vk);
|
||||
let kzg_data = KzgData::from((kzg_vk, Some(kzg_pk.powers_of_g[0..3].to_vec())));
|
||||
let nova_cyclefold_data = NovaCyclefoldData::from((g16_data, kzg_data));
|
||||
|
||||
let g16_proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();
|
||||
|
||||
let (a_x, a_y) = g16_proof.a.xy().unwrap();
|
||||
let (b_x, b_y) = g16_proof.b.xy().unwrap();
|
||||
let (c_x, c_y) = g16_proof.c.xy().unwrap();
|
||||
|
||||
let poseidon_config = poseidon_test_config::<Fr>();
|
||||
let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
|
||||
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
|
||||
|
||||
let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||
.take(DEFAULT_SETUP_LEN)
|
||||
.collect();
|
||||
let cm = KZGProver::<G1>::commit(&kzg_pk, &v, &Fr::zero()).unwrap();
|
||||
let (eval, proof) =
|
||||
KZGProver::<G1>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap();
|
||||
|
||||
let decider_template = HeaderInclusion::<NovaCyclefoldDecider>::builder()
|
||||
.template(nova_cyclefold_data)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap();
|
||||
|
||||
let nova_cyclefold_verifier_bytecode = compile_solidity(decider_template, "NovaDecider");
|
||||
|
||||
let mut evm = Evm::default();
|
||||
let verifier_address = evm.create(nova_cyclefold_verifier_bytecode);
|
||||
|
||||
let (cm_affine, proof_affine) = (cm.into_affine(), proof.into_affine());
|
||||
let (x_comm, y_comm) = cm_affine.xy().unwrap();
|
||||
let (x_proof, y_proof) = proof_affine.xy().unwrap();
|
||||
let y = eval.into_bigint().to_bytes_be();
|
||||
|
||||
transcript_v.absorb_point(&cm).unwrap();
|
||||
let x = transcript_v.get_challenge();
|
||||
|
||||
let x = x.into_bigint().to_bytes_be();
|
||||
let mut calldata: Vec<u8> = chain![
|
||||
FUNCTION_SIGNATURE_NOVA_CYCLEFOLD_CHECK,
|
||||
a_x.into_bigint().to_bytes_be(),
|
||||
a_y.into_bigint().to_bytes_be(),
|
||||
b_x.c1.into_bigint().to_bytes_be(),
|
||||
b_x.c0.into_bigint().to_bytes_be(),
|
||||
b_y.c1.into_bigint().to_bytes_be(),
|
||||
b_y.c0.into_bigint().to_bytes_be(),
|
||||
c_x.into_bigint().to_bytes_be(),
|
||||
c_y.into_bigint().to_bytes_be(),
|
||||
BigInt::from(Fr::from(circuit.z)).to_bytes_be(),
|
||||
x_comm.into_bigint().to_bytes_be(),
|
||||
y_comm.into_bigint().to_bytes_be(),
|
||||
x_proof.into_bigint().to_bytes_be(),
|
||||
y_proof.into_bigint().to_bytes_be(),
|
||||
x.clone(),
|
||||
y,
|
||||
]
|
||||
.collect();
|
||||
|
||||
let (_, output) = evm.call(verifier_address, calldata.clone());
|
||||
assert_eq!(*output.last().unwrap(), 1);
|
||||
|
||||
// change calldata to make it invalid
|
||||
let last_calldata_element = calldata.last_mut().unwrap();
|
||||
*last_calldata_element = 0;
|
||||
let (_, output) = evm.call(verifier_address, calldata);
|
||||
assert_eq!(*output.last().unwrap(), 0);
|
||||
}
|
||||
}
|
||||
69
solidity-verifiers/src/verifiers/nova_cyclefold.rs
Normal file
69
solidity-verifiers/src/verifiers/nova_cyclefold.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use crate::utils::HeaderInclusion;
|
||||
use crate::{Groth16Data, KzgData, ProtocolData, PRAGMA_GROTH16_VERIFIER};
|
||||
use ark_bn254::{Bn254, G1Affine};
|
||||
use ark_groth16::VerifyingKey;
|
||||
use ark_poly_commit::kzg10::VerifierKey;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use askama::Template;
|
||||
|
||||
use super::g16::Groth16Verifier;
|
||||
use super::kzg::KZG10Verifier;
|
||||
|
||||
#[derive(Template, Default)]
|
||||
#[template(path = "nova_cyclefold_decider.askama.sol", ext = "sol")]
|
||||
pub(crate) struct NovaCyclefoldDecider {
|
||||
groth16_verifier: Groth16Verifier,
|
||||
kzg10_verifier: KZG10Verifier,
|
||||
}
|
||||
|
||||
impl From<NovaCyclefoldData> for NovaCyclefoldDecider {
|
||||
fn from(value: NovaCyclefoldData) -> Self {
|
||||
Self {
|
||||
groth16_verifier: Groth16Verifier::from(value.g16_data),
|
||||
kzg10_verifier: KZG10Verifier::from(value.kzg_data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(CanonicalDeserialize, CanonicalSerialize, PartialEq, Debug)]
|
||||
pub struct NovaCyclefoldData {
|
||||
g16_data: Groth16Data,
|
||||
kzg_data: KzgData,
|
||||
}
|
||||
|
||||
impl ProtocolData for NovaCyclefoldData {
|
||||
const PROTOCOL_NAME: &'static str = "NovaCyclefold";
|
||||
|
||||
fn render_as_template(self, pragma: Option<String>) -> Vec<u8> {
|
||||
HeaderInclusion::<NovaCyclefoldDecider>::builder()
|
||||
.pragma_version(pragma.unwrap_or(PRAGMA_GROTH16_VERIFIER.to_string()))
|
||||
.template(self)
|
||||
.build()
|
||||
.render()
|
||||
.unwrap()
|
||||
.into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Groth16Data, KzgData)> for NovaCyclefoldData {
|
||||
fn from(value: (Groth16Data, KzgData)) -> Self {
|
||||
Self {
|
||||
g16_data: value.0,
|
||||
kzg_data: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NovaCyclefoldData {
|
||||
pub fn new(
|
||||
vkey_g16: VerifyingKey<Bn254>,
|
||||
vkey_kzg: VerifierKey<Bn254>,
|
||||
crs_points: Vec<G1Affine>,
|
||||
) -> Self {
|
||||
Self {
|
||||
g16_data: Groth16Data::from(vkey_g16),
|
||||
// TODO: Remove option from crs points
|
||||
kzg_data: KzgData::from((vkey_kzg, Some(crs_points))),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user