From a440ff913adb0d8b11e508bdbecd30b43383c8d0 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Thu, 14 Sep 2023 00:58:55 -0700 Subject: [PATCH] feat: change yul code into Solidity assembly (#47) * feat: change yul code into Solidity assembly Just changes to wrapping yul in solidity assembly block * chore: loosen pragma * chore: remove example sol file --- snark-verifier-sdk/Cargo.toml | 19 ++++-- snark-verifier-sdk/examples/standard_plonk.rs | 30 +++------ snark-verifier-sdk/src/evm.rs | 8 +-- snark-verifier/Cargo.toml | 7 +- .../examples/evm-verifier-with-accumulator.rs | 2 +- snark-verifier/examples/evm-verifier.rs | 2 +- snark-verifier/src/loader/evm.rs | 4 +- snark-verifier/src/loader/evm/code.rs | 66 +++++++++---------- snark-verifier/src/loader/evm/loader.rs | 18 +++-- snark-verifier/src/loader/evm/util.rs | 5 +- .../src/system/halo2/test/kzg/evm.rs | 4 +- 11 files changed, 80 insertions(+), 85 deletions(-) diff --git a/snark-verifier-sdk/Cargo.toml b/snark-verifier-sdk/Cargo.toml index f3ac32f7..2f1ba1a3 100644 --- a/snark-verifier-sdk/Cargo.toml +++ b/snark-verifier-sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snark-verifier-sdk" -version = "0.1.1" +version = "0.1.2" edition = "2021" [dependencies] @@ -21,14 +21,16 @@ snark-verifier = { path = "../snark-verifier", default-features = false } # system_halo2 halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2", tag = "v2023_04_20" } # not optional for now -halo2curves = { git = 'https://github.com/privacy-scaling-explorations/halo2curves', tag = "0.3.2" } # must be same version as in halo2_proofs +halo2curves = { git = 'https://github.com/privacy-scaling-explorations/halo2curves', tag = "0.3.2" } # must be same version as in halo2_proofs # loader_halo2 halo2_wrong_ecc = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20", package = "ecc", optional = true } poseidon = { git = "https://github.com/privacy-scaling-explorations/poseidon", tag = "v2023_04_20", optional = true } # loader_evm -ethereum-types = { version = "0.14", default-features = false, features = ["std"], optional = true } +ethereum-types = { version = "0.14", default-features = false, features = [ + "std", +], optional = true } [dev-dependencies] ark-std = { version = "0.3.0", features = ["print-trace"] } @@ -41,12 +43,17 @@ criterion-macro = "0.4" default = ["loader_halo2", "loader_evm", "derive_serde", "display"] display = ["dep:ark-std"] loader_evm = ["snark-verifier/loader_evm", "dep:ethereum-types"] -loader_halo2 = ["snark-verifier/system_halo2", "snark-verifier/loader_halo2", "dep:halo2_wrong_ecc", "dep:poseidon"] +loader_halo2 = [ + "snark-verifier/system_halo2", + "snark-verifier/loader_halo2", + "dep:halo2_wrong_ecc", + "dep:poseidon", +] parallel = ["snark-verifier/parallel"] derive_serde = ["snark-verifier/derive_serde", "halo2curves/derive_serde"] halo2_circuit_params = ["snark-verifier/halo2_circuit_params"] [[bench]] + name = "standard_plonk" -required-features = ["loader_halo2"] -harness = false +required-features = ["loader_halo2", "loader_evm"] diff --git a/snark-verifier-sdk/examples/standard_plonk.rs b/snark-verifier-sdk/examples/standard_plonk.rs index d25d51bc..03c36f02 100644 --- a/snark-verifier-sdk/examples/standard_plonk.rs +++ b/snark-verifier-sdk/examples/standard_plonk.rs @@ -170,27 +170,15 @@ fn main() { ); end_timer!(start0); - std::fs::remove_file("./examples/agg.snark").unwrap_or_default(); - let _snark = gen_snark_shplonk( + let num_instances = agg_circuit.num_instance(); + let instances = agg_circuit.instances(); + let proof_calldata = gen_evm_proof_shplonk(¶ms, &pk, agg_circuit, instances.clone()); + + let deployment_code = gen_evm_verifier_shplonk::>( ¶ms, - &pk, - agg_circuit.clone(), - Some(Path::new("./examples/agg.snark")), + pk.get_vk(), + num_instances, + Some(Path::new("./examples/StandardPlonkVerifierExample.sol")), ); - - #[cfg(feature = "loader_evm")] - { - // do one more time to verify - let num_instances = agg_circuit.num_instance(); - let instances = agg_circuit.instances(); - let proof_calldata = gen_evm_proof_shplonk(¶ms, &pk, agg_circuit, instances.clone()); - - let deployment_code = gen_evm_verifier_shplonk::>( - ¶ms, - pk.get_vk(), - num_instances, - Some(Path::new("./examples/standard_plonk.yul")), - ); - evm_verify(deployment_code, instances, proof_calldata); - } + evm_verify(deployment_code, instances, proof_calldata); } diff --git a/snark-verifier-sdk/src/evm.rs b/snark-verifier-sdk/src/evm.rs index 841c567b..8cb703ce 100644 --- a/snark-verifier-sdk/src/evm.rs +++ b/snark-verifier-sdk/src/evm.rs @@ -22,7 +22,7 @@ use itertools::Itertools; use rand::{rngs::StdRng, SeedableRng}; pub use snark_verifier::loader::evm::encode_calldata; use snark_verifier::{ - loader::evm::{compile_yul, deploy_and_call, EvmLoader}, + loader::evm::{compile_solidity, deploy_and_call, EvmLoader}, pcs::{ kzg::{KzgAccumulator, KzgAsVerifyingKey, KzgDecidingKey, KzgSuccinctVerifyingKey}, AccumulationDecider, AccumulationScheme, PolynomialCommitmentScheme, @@ -148,13 +148,13 @@ where PlonkVerifier::::read_proof(&dk, &protocol, &instances, &mut transcript).unwrap(); PlonkVerifier::::verify(&dk, &protocol, &instances, &proof).unwrap(); - let yul_code = loader.yul_code(); - let byte_code = compile_yul(&yul_code); + let sol_code = loader.solidity_code(); + let byte_code = compile_solidity(&sol_code); if let Some(path) = path { path.parent() .and_then(|dir| fs::create_dir_all(dir).ok()) .unwrap(); - fs::write(path, yul_code).unwrap(); + fs::write(path, sol_code).unwrap(); } byte_code } diff --git a/snark-verifier/Cargo.toml b/snark-verifier/Cargo.toml index 735f3295..2777f66c 100644 --- a/snark-verifier/Cargo.toml +++ b/snark-verifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snark-verifier" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dependencies] @@ -50,7 +50,10 @@ loader_halo2 = ["dep:halo2_proofs", "dep:halo2_wrong_ecc", "dep:poseidon"] system_halo2 = ["dep:halo2_proofs"] # features of halo2 -halo2_circuit_params = ["halo2_proofs?/circuit-params", "halo2_wrong_ecc?/circuit-params"] +halo2_circuit_params = [ + "halo2_proofs?/circuit-params", + "halo2_wrong_ecc?/circuit-params", +] derive_serde = ["dep:serde"] [[example]] diff --git a/snark-verifier/examples/evm-verifier-with-accumulator.rs b/snark-verifier/examples/evm-verifier-with-accumulator.rs index f0ec0afa..a4415297 100644 --- a/snark-verifier/examples/evm-verifier-with-accumulator.rs +++ b/snark-verifier/examples/evm-verifier-with-accumulator.rs @@ -582,7 +582,7 @@ fn gen_aggregation_evm_verifier( let proof = PlonkVerifier::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); PlonkVerifier::verify(&vk, &protocol, &instances, &proof).unwrap(); - evm::compile_yul(&loader.yul_code()) + evm::compile_solidity(&loader.solidity_code()) } fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { diff --git a/snark-verifier/examples/evm-verifier.rs b/snark-verifier/examples/evm-verifier.rs index 3a0a412c..cc011560 100644 --- a/snark-verifier/examples/evm-verifier.rs +++ b/snark-verifier/examples/evm-verifier.rs @@ -225,7 +225,7 @@ fn gen_evm_verifier( let proof = PlonkVerifier::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); PlonkVerifier::verify(&vk, &protocol, &instances, &proof).unwrap(); - evm::compile_yul(&loader.yul_code()) + evm::compile_solidity(&loader.solidity_code()) } fn evm_verify(deployment_code: Vec, instances: Vec>, proof: Vec) { diff --git a/snark-verifier/src/loader/evm.rs b/snark-verifier/src/loader/evm.rs index 74ed0f4e..19b145bb 100644 --- a/snark-verifier/src/loader/evm.rs +++ b/snark-verifier/src/loader/evm.rs @@ -6,6 +6,6 @@ pub(crate) mod util; pub use loader::{EcPoint, EvmLoader, Scalar}; pub use util::{ - compile_yul, deploy_and_call, encode_calldata, estimate_gas, fe_to_u256, modulus, u256_to_fe, - Address, B256, U256, U512, + compile_solidity, deploy_and_call, encode_calldata, estimate_gas, fe_to_u256, modulus, + u256_to_fe, Address, B256, U256, U512, }; diff --git a/snark-verifier/src/loader/evm/code.rs b/snark-verifier/src/loader/evm/code.rs index 2fec71d2..d768d795 100644 --- a/snark-verifier/src/loader/evm/code.rs +++ b/snark-verifier/src/loader/evm/code.rs @@ -6,14 +6,14 @@ pub enum Precompiled { } #[derive(Clone, Debug)] -pub struct YulCode { +pub struct SolidityAssemblyCode { // runtime code area runtime: String, } -impl YulCode { +impl SolidityAssemblyCode { pub fn new() -> Self { - YulCode { + Self { runtime: String::new(), } } @@ -21,42 +21,36 @@ impl YulCode { pub fn code(&self, base_modulus: String, scalar_modulus: String) -> String { format!( " - object \"plonk_verifier\" {{ - code {{ - function allocate(size) -> ptr {{ - ptr := mload(0x40) - if eq(ptr, 0) {{ ptr := 0x60 }} - mstore(0x40, add(ptr, size)) +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract Halo2Verifier {{ + fallback(bytes calldata) external returns (bytes memory) {{ + assembly {{ + let success := true + let f_p := {base_modulus} + let f_q := {scalar_modulus} + function validate_ec_point(x, y) -> valid {{ + {{ + let x_lt_p := lt(x, {base_modulus}) + let y_lt_p := lt(y, {base_modulus}) + valid := and(x_lt_p, y_lt_p) }} - let size := datasize(\"Runtime\") - let offset := allocate(size) - datacopy(offset, dataoffset(\"Runtime\"), size) - return(offset, size) - }} - object \"Runtime\" {{ - code {{ - let success:bool := true - let f_p := {base_modulus} - let f_q := {scalar_modulus} - function validate_ec_point(x, y) -> valid:bool {{ - {{ - let x_lt_p:bool := lt(x, {base_modulus}) - let y_lt_p:bool := lt(y, {base_modulus}) - valid := and(x_lt_p, y_lt_p) - }} - {{ - let y_square := mulmod(y, y, {base_modulus}) - let x_square := mulmod(x, x, {base_modulus}) - let x_cube := mulmod(x_square, x, {base_modulus}) - let x_cube_plus_3 := addmod(x_cube, 3, {base_modulus}) - let is_affine:bool := eq(x_cube_plus_3, y_square) - valid := and(valid, is_affine) - }} - }} - {} + {{ + let y_square := mulmod(y, y, {base_modulus}) + let x_square := mulmod(x, x, {base_modulus}) + let x_cube := mulmod(x_square, x, {base_modulus}) + let x_cube_plus_3 := addmod(x_cube, 3, {base_modulus}) + let is_affine := eq(x_cube_plus_3, y_square) + valid := and(valid, is_affine) }} }} - }}", + {} + }} + }} +}} + ", self.runtime ) } diff --git a/snark-verifier/src/loader/evm/loader.rs b/snark-verifier/src/loader/evm/loader.rs index 44d5dd27..c80d5892 100644 --- a/snark-verifier/src/loader/evm/loader.rs +++ b/snark-verifier/src/loader/evm/loader.rs @@ -1,7 +1,7 @@ use crate::{ loader::{ evm::{ - code::{Precompiled, YulCode}, + code::{Precompiled, SolidityAssemblyCode}, fe_to_u256, modulus, u256_to_fe, U256, U512, }, EcPointLoader, LoadedEcPoint, LoadedScalar, Loader, ScalarLoader, @@ -53,7 +53,7 @@ impl Value { pub struct EvmLoader { base_modulus: U256, scalar_modulus: U256, - code: RefCell, + code: RefCell, ptr: RefCell, cache: RefCell>, } @@ -71,7 +71,7 @@ impl EvmLoader { { let base_modulus = modulus::(); let scalar_modulus = modulus::(); - let code = YulCode::new(); + let code = SolidityAssemblyCode::new(); Rc::new(Self { base_modulus, @@ -82,10 +82,14 @@ impl EvmLoader { }) } - /// Returns generated yul code. - pub fn yul_code(self: &Rc) -> String { + /// Returns generated Solidity code. This is "Solidity" code that is wrapped in an assembly block. + /// In other words, it's basically just assembly (equivalently, Yul). + pub fn solidity_code(self: &Rc) -> String { let code = " - if not(success) { revert(0, 0) } + // Revert if anything fails + if iszero(success) { revert(0, 0) } + + // Return empty bytes on success return(0, 0)" .to_string(); self.code.borrow_mut().runtime_append(code); @@ -106,7 +110,7 @@ impl EvmLoader { *self.ptr.borrow() } - pub(crate) fn code_mut(&self) -> impl DerefMut + '_ { + pub(crate) fn code_mut(&self) -> impl DerefMut + '_ { self.code.borrow_mut() } diff --git a/snark-verifier/src/loader/evm/util.rs b/snark-verifier/src/loader/evm/util.rs index 78827cb2..7247d8a3 100644 --- a/snark-verifier/src/loader/evm/util.rs +++ b/snark-verifier/src/loader/evm/util.rs @@ -101,13 +101,12 @@ pub fn estimate_gas(cost: Cost) -> usize { intrinsic_cost + calldata_cost + ec_operation_cost } -/// Compile given yul `code` into deployment bytecode. -pub fn compile_yul(code: &str) -> Vec { +/// Compile given Solidity `code` into deployment bytecode. +pub fn compile_solidity(code: &str) -> Vec { let mut cmd = Command::new("solc") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .arg("--bin") - .arg("--yul") .arg("-") .spawn() .unwrap(); diff --git a/snark-verifier/src/system/halo2/test/kzg/evm.rs b/snark-verifier/src/system/halo2/test/kzg/evm.rs index 5842914d..3ec00032 100644 --- a/snark-verifier/src/system/halo2/test/kzg/evm.rs +++ b/snark-verifier/src/system/halo2/test/kzg/evm.rs @@ -24,7 +24,7 @@ macro_rules! halo2_kzg_evm_verify { use halo2_proofs::poly::commitment::ParamsProver; use std::rc::Rc; use $crate::{ - loader::evm::{compile_yul, deploy_and_call, encode_calldata, EvmLoader}, + loader::evm::{compile_solidity, deploy_and_call, encode_calldata, EvmLoader}, system::halo2::{ test::kzg::{BITS, LIMBS}, transcript::evm::EvmTranscript, @@ -48,7 +48,7 @@ macro_rules! halo2_kzg_evm_verify { <$plonk_verifier>::read_proof(&vk, &protocol, &instances, &mut transcript).unwrap(); <$plonk_verifier>::verify(&vk, &protocol, &instances, &proof).unwrap(); - compile_yul(&loader.yul_code()) + compile_solidity(&loader.solidity_code()) }; let calldata = encode_calldata($instances, &$proof);