Skip to content

Commit

Permalink
Opaque Solidity verifier (#213)
Browse files Browse the repository at this point in the history
* Introduce interface, provide implementation

* Fix contract compilation

* Make binary finding work with multiple solc outputs

* clippy

* typo

* fix verifier tests

* Remove leftovers

* Create new module for calldata. Enum for API

* Pass simply state len to the selector computation

* Move calldata code to the new module

* Add mode argument to the selector computing function

* Move calldata module up (from utils)

* Refactor unit tests a bit

* Fix selector computation

* Fix examples

* NovaDecider is OpaqueVerifier

* Add override
  • Loading branch information
pmikolajczyk41 authored Feb 26, 2025
1 parent 84336de commit 091fe94
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 138 deletions.
14 changes: 9 additions & 5 deletions examples/circom_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ use folding_schemes::{
transcript::poseidon::poseidon_canonical_config,
Decider, Error, FoldingScheme,
};
use solidity_verifiers::calldata::{
get_function_selector_for_nova_cyclefold_verifier, NovaVerificationMode,
};
use solidity_verifiers::{
evm::{compile_solidity, Evm},
utils::get_function_selector_for_nova_cyclefold_verifier,
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
NovaCycleFoldVerifierKey,
};
Expand Down Expand Up @@ -133,8 +135,10 @@ fn main() -> Result<(), Error> {
println!("Decider proof verification: {}", verified);

// Now, let's generate the Solidity code that verifies this Decider final proof
let function_selector =
get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
let function_selector = get_function_selector_for_nova_cyclefold_verifier(
NovaVerificationMode::Explicit,
nova.z_0.len(),
);

let calldata: Vec<u8> = prepare_calldata(
function_selector,
Expand All @@ -143,7 +147,7 @@ fn main() -> Result<(), Error> {
nova.z_i,
&nova.U_i,
&nova.u_i,
proof,
&proof,
)?;

// prepare the setup params for the solidity verifier
Expand All @@ -167,7 +171,7 @@ fn main() -> Result<(), Error> {
decider_solidity_code.clone(),
)?;
fs::write("./examples/solidity-calldata.calldata", calldata.clone())?;
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
let s = solidity_verifiers::calldata::get_formatted_calldata(calldata.clone());
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
Ok(())
}
14 changes: 9 additions & 5 deletions examples/full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ use folding_schemes::{
transcript::poseidon::poseidon_canonical_config,
Decider, Error, FoldingScheme,
};
use solidity_verifiers::calldata::{
get_function_selector_for_nova_cyclefold_verifier, NovaVerificationMode,
};
use solidity_verifiers::{
evm::{compile_solidity, Evm},
utils::get_function_selector_for_nova_cyclefold_verifier,
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
NovaCycleFoldVerifierKey,
};
Expand Down Expand Up @@ -118,8 +120,10 @@ fn main() -> Result<(), Error> {
println!("Decider proof verification: {}", verified);

// Now, let's generate the Solidity code that verifies this Decider final proof
let function_selector =
get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
let function_selector = get_function_selector_for_nova_cyclefold_verifier(
NovaVerificationMode::Explicit,
nova.z_0.len(),
);

let calldata: Vec<u8> = prepare_calldata(
function_selector,
Expand All @@ -128,7 +132,7 @@ fn main() -> Result<(), Error> {
nova.z_i,
&nova.U_i,
&nova.u_i,
proof,
&proof,
)?;

// prepare the setup params for the solidity verifier
Expand All @@ -152,7 +156,7 @@ fn main() -> Result<(), Error> {
decider_solidity_code.clone(),
)?;
fs::write("./examples/solidity-calldata.calldata", calldata.clone())?;
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
let s = solidity_verifiers::calldata::get_formatted_calldata(calldata.clone());
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
Ok(())
}
14 changes: 9 additions & 5 deletions examples/noir_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ use folding_schemes::{
};
use std::{path::Path, time::Instant};

use solidity_verifiers::calldata::{
get_function_selector_for_nova_cyclefold_verifier, NovaVerificationMode,
};
use solidity_verifiers::{
evm::{compile_solidity, Evm},
utils::get_function_selector_for_nova_cyclefold_verifier,
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
NovaCycleFoldVerifierKey,
};
Expand Down Expand Up @@ -105,8 +107,10 @@ fn main() -> Result<(), Error> {
println!("Decider proof verification: {}", verified);

// Now, let's generate the Solidity code that verifies this Decider final proof
let function_selector =
get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
let function_selector = get_function_selector_for_nova_cyclefold_verifier(
NovaVerificationMode::Explicit,
nova.z_0.len(),
);

let calldata: Vec<u8> = prepare_calldata(
function_selector,
Expand All @@ -115,7 +119,7 @@ fn main() -> Result<(), Error> {
nova.z_i,
&nova.U_i,
&nova.u_i,
proof,
&proof,
)?;

// prepare the setup params for the solidity verifier
Expand All @@ -139,7 +143,7 @@ fn main() -> Result<(), Error> {
decider_solidity_code.clone(),
)?;
fs::write("./examples/solidity-calldata.calldata", calldata.clone())?;
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
let s = solidity_verifiers::calldata::get_formatted_calldata(calldata.clone());
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
Ok(())
}
14 changes: 9 additions & 5 deletions examples/noname_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ use folding_schemes::{
};
use std::time::Instant;

use solidity_verifiers::calldata::{
get_function_selector_for_nova_cyclefold_verifier, NovaVerificationMode,
};
use solidity_verifiers::{
evm::{compile_solidity, Evm},
utils::get_function_selector_for_nova_cyclefold_verifier,
verifiers::nova_cyclefold::get_decider_template_for_cyclefold_decider,
NovaCycleFoldVerifierKey,
};
Expand Down Expand Up @@ -126,8 +128,10 @@ fn main() -> Result<(), Error> {
println!("Decider proof verification: {}", verified);

// Now, let's generate the Solidity code that verifies this Decider final proof
let function_selector =
get_function_selector_for_nova_cyclefold_verifier(nova.z_0.len() * 2 + 1);
let function_selector = get_function_selector_for_nova_cyclefold_verifier(
NovaVerificationMode::Explicit,
nova.z_0.len(),
);

let calldata: Vec<u8> = prepare_calldata(
function_selector,
Expand All @@ -136,7 +140,7 @@ fn main() -> Result<(), Error> {
nova.z_i,
&nova.U_i,
&nova.u_i,
proof,
&proof,
)?;

// prepare the setup params for the solidity verifier
Expand All @@ -160,7 +164,7 @@ fn main() -> Result<(), Error> {
decider_solidity_code.clone(),
)?;
fs::write("./examples/solidity-calldata.calldata", calldata.clone())?;
let s = solidity_verifiers::utils::get_formatted_calldata(calldata.clone());
let s = solidity_verifiers::calldata::get_formatted_calldata(calldata.clone());
fs::write("./examples/solidity-calldata.inputs", s.join(",\n")).expect("");
Ok(())
}
4 changes: 2 additions & 2 deletions folding-schemes/src/folding/nova/decider_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ where
cmT: C,
r: C::ScalarField,
// the KZG challenges are provided by the prover, but in-circuit they are checked to match
// the in-circuit computed computed ones.
// the in-circuit computed ones.
kzg_challenges: [C::ScalarField; 2],
}

Expand Down Expand Up @@ -253,7 +253,7 @@ pub fn prepare_calldata(
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>>,
proof: &Proof<ark_bn254::G1Projective, KZG<Bn254>, Groth16<Bn254>>,
) -> Result<Vec<u8>, Error> {
Ok([
function_signature_check.to_eth(),
Expand Down
52 changes: 52 additions & 0 deletions solidity-verifiers/src/calldata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crypto::digest::Digest;
use crypto::sha3::Sha3;
use num_bigint::BigUint;

/// Specifies which API to use for a proof verification in a contract.
#[derive(Copy, Clone, Debug, Default)]
pub enum NovaVerificationMode {
/// Use the `verifyNovaProof` function.
#[default]
Explicit,
/// Use the `verifyOpaqueNovaProof` function.
Opaque,
/// Use the `verifyOpaqueNovaProofWithInputs` function.
OpaqueWithInputs,
}

/// Formats call data from a vec of bytes to a hashmap
/// Useful for debugging directly on the EVM
/// !! Should follow the contract's function signature, we assume the order of arguments is correct
pub fn get_formatted_calldata(calldata: Vec<u8>) -> Vec<String> {
let mut formatted_calldata = vec![];
for i in (4..calldata.len()).step_by(32) {
let val = BigUint::from_bytes_be(&calldata[i..i + 32]);
formatted_calldata.push(format!("{}", val));
}
formatted_calldata
}

/// Computes the function selector for the nova cyclefold verifier.
/// It is computed on the fly since it depends on the IVC state length.
pub fn get_function_selector_for_nova_cyclefold_verifier(
mode: NovaVerificationMode,
state_len: usize,
) -> [u8; 4] {
let fn_sig = match mode {
NovaVerificationMode::Explicit =>
format!(
"verifyNovaProof(uint256[{}],uint256[4],uint256[2],uint256[3],uint256[2],uint256[2][2],uint256[2],uint256[4],uint256[2][2])",
state_len * 2 + 1
),
NovaVerificationMode::Opaque =>
format!("verifyOpaqueNovaProof(uint256[{}])", 26 + 2 * state_len),
NovaVerificationMode::OpaqueWithInputs =>
format!("verifyOpaqueNovaProofWithInputs(uint256,uint256[{state_len}],uint256[{state_len}],uint256[25])"),
};

let mut hasher = Sha3::keccak256();
hasher.input_str(&fn_sig);
let hash = &mut [0u8; 32];
hasher.result(hash);
[hash[0], hash[1], hash[2], hash[3]]
}
12 changes: 8 additions & 4 deletions solidity-verifiers/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,14 @@ pub fn compile_solidity(solidity: impl AsRef<[u8]>, contract_name: &str) -> Vec<
/// `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())
let intro_str = format!("======= <stdin>:{contract_name} =======\nBinary:\n");
let start = stdout.find(&intro_str)?;
let end = stdout[start + intro_str.len()..]
.find('\n')
.map(|pos| pos + start + intro_str.len())
.unwrap_or(stdout.len());
let binary_section = stdout[start + intro_str.len()..end].trim();
Some(hex::decode(binary_section).unwrap())
}

/// Evm runner.
Expand Down
1 change: 1 addition & 0 deletions solidity-verifiers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod calldata;
pub mod evm;
pub mod utils;
pub mod verifiers;
Expand Down
28 changes: 1 addition & 27 deletions solidity-verifiers/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,7 @@
use crate::{GPL3_SDPX_IDENTIFIER, PRAGMA_GROTH16_VERIFIER};
use askama::Template;
use crypto::{digest::Digest, sha3::Sha3};
use num_bigint::BigUint;
pub mod encoding;

/// Formats call data from a vec of bytes to a hashmap
/// Useful for debugging directly on the EVM
/// !! Should follow the contract's function signature, we assuming the order of arguments is correct
pub fn get_formatted_calldata(calldata: Vec<u8>) -> Vec<String> {
let mut formatted_calldata = vec![];
for i in (4..calldata.len()).step_by(32) {
let val = BigUint::from_bytes_be(&calldata[i..i + 32]);
formatted_calldata.push(format!("{}", val));
}
formatted_calldata
}

/// Computes the function selector for the nova cyclefold verifier
/// It is computed on the fly since it depends on the length of the first parameter array
pub fn get_function_selector_for_nova_cyclefold_verifier(
first_param_array_length: usize,
) -> [u8; 4] {
let mut hasher = Sha3::keccak256();
let fn_sig = format!("verifyNovaProof(uint256[{}],uint256[4],uint256[2],uint256[3],uint256[2],uint256[2][2],uint256[2],uint256[4],uint256[2][2])", first_param_array_length);
hasher.input_str(&fn_sig);
let hash = &mut [0u8; 32];
hasher.result(hash);
[hash[0], hash[1], hash[2], hash[3]]
}
pub mod encoding;

#[derive(Template)]
#[template(path = "header_template.askama.sol", ext = "sol")]
Expand Down
2 changes: 1 addition & 1 deletion solidity-verifiers/src/verifiers/g16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ mod tests {
let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();
let res = Groth16Verifier::from(g16_vk).render().unwrap();
save_solidity("groth16_verifier.sol", &res);
let groth16_verifier_bytecode = compile_solidity(&res, "Verifier");
let groth16_verifier_bytecode = compile_solidity(&res, "Groth16Verifier");
let mut evm = Evm::default();
let verifier_address = evm.create(groth16_verifier_bytecode);
let (a_x, a_y) = proof.a.xy().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions solidity-verifiers/src/verifiers/kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ mod tests {
.render()
.unwrap();

let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
let kzg_verifier_bytecode = compile_solidity(res, "KZG10Verifier");
let mut evm = Evm::default();
_ = evm.create(kzg_verifier_bytecode);
}
Expand All @@ -147,7 +147,7 @@ mod tests {
.render()
.unwrap();

let kzg_verifier_bytecode = compile_solidity(template, "KZG10");
let kzg_verifier_bytecode = compile_solidity(template, "KZG10Verifier");
let mut evm = Evm::default();
let verifier_address = evm.create(kzg_verifier_bytecode);

Expand Down
Loading

0 comments on commit 091fe94

Please sign in to comment.