From 3e1b3a6f3b6646ff49162672dd31e6d8a77c6462 Mon Sep 17 00:00:00 2001 From: pd <5864173+paolodamico@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:08:14 +0100 Subject: [PATCH 1/2] use ruint & alloy for ethereum compat --- Cargo.toml | 9 +++--- README.md | 7 ++--- src/ethereum.rs | 7 ++--- tests/solidity.rs | 80 +++++++++++++++++------------------------------ 4 files changed, 39 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6118ef7..4c6c451 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,13 +33,14 @@ ark-serialize = { version = "0.4.2", default-features = false } hex = "0.4.3" byteorder = "1.4.3" -# ethereum compat -ethers-core = { version = "2.0.7", default-features = false, optional = true } # error handling thiserror = "1.0.39" color-eyre = "0.6.2" +# ethereum compat +ruint = { version = "1.12.3", default-features = false ,optional = true, features=["ark-ff-04"] } + cfg-if = "1.0.0" [dev-dependencies] @@ -47,7 +48,7 @@ criterion = "0.5.1" hex-literal = "0.4.1" tokio = { version = "1.29.1", features = ["macros"] } serde_json = "1.0.94" -ethers = "2.0.7" +alloy = { version = "0.6.4", default-features = false, features = ["contract", "node-bindings"] } [[bench]] name = "groth16" @@ -58,4 +59,4 @@ default = ["wasmer/default", "circom-2", "ethereum"] wasm = ["wasmer/js-default"] bench-complex-all = [] circom-2 = [] -ethereum = ["ethers-core"] +ethereum = ["ruint"] diff --git a/README.md b/README.md index 0bbc9bb..3ecc3e8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Arkworks bindings to Circom's R1CS, for Groth16 Proof and Witness generation in Rust. -![Github Actions](https://github.com/gakonst/ark-circom/workflows/Tests/badge.svg) +![Github Actions](https://github.com/arkworks-rs/circom-compat/workflows/Tests/badge.svg) ## Documentation @@ -13,7 +13,7 @@ Clone the repository and run `cd ark-circom/ && cargo doc --open` ```toml [dependencies] -ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } +ark-circom = { git = "https://github.com/arkworks-rs/ark-circom.git" } ``` ## Example @@ -55,8 +55,7 @@ assert!(verified); Tests require the following installed: -1. [`solc`](https://solidity.readthedocs.io/en/latest/installing-solidity.html). We also recommend using [solc-select](https://github.com/crytic/solc-select) for more flexibility. -2. [`ganache-cli`](https://github.com/trufflesuite/ganache-cli#installation) +1. [`anvil`](https://book.getfoundry.sh/getting-started/installation). ## Features diff --git a/src/ethereum.rs b/src/ethereum.rs index 9d8908a..2d3cd70 100644 --- a/src/ethereum.rs +++ b/src/ethereum.rs @@ -1,11 +1,11 @@ //! Helpers for converting Arkworks types to U256-tuples as expected by the //! Solidity Groth16 Verifier smart contracts use ark_ff::{BigInteger, PrimeField}; -use ethers_core::types::U256; use num_traits::Zero; use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine}; use ark_serialize::CanonicalDeserialize; +use ruint::aliases::U256; pub struct Inputs(pub Vec); @@ -174,8 +174,7 @@ impl From for ark_groth16::VerifyingKey { // Helper for converting a PrimeField to its U256 representation for Ethereum compatibility fn u256_to_point(point: U256) -> F { - let mut buf = [0; 32]; - point.to_little_endian(&mut buf); + let buf: [u8; 32] = point.to_le_bytes(); let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works"); F::from_bigint(bigint).expect("always works") } @@ -185,7 +184,7 @@ fn u256_to_point(point: U256) -> F { fn point_to_u256(point: F) -> U256 { let point = point.into_bigint(); let point_bytes = point.to_bytes_be(); - U256::from(&point_bytes[..]) + U256::try_from_be_slice(&point_bytes[..]).expect("always works") } #[cfg(test)] diff --git a/tests/solidity.rs b/tests/solidity.rs index bd7235d..1c1e453 100644 --- a/tests/solidity.rs +++ b/tests/solidity.rs @@ -1,3 +1,4 @@ +use alloy::{providers::ProviderBuilder, sol}; use ark_circom::{ethereum, CircomBuilder, CircomConfig}; use ark_std::rand::thread_rng; use color_eyre::Result; @@ -6,13 +7,9 @@ use ark_bn254::{Bn254, Fr}; use ark_crypto_primitives::snark::SNARK; use ark_groth16::Groth16; -use ethers::{ - contract::ContractError, - prelude::abigen, - providers::{Http, Middleware, Provider}, - utils::Anvil, -}; -use std::{convert::TryFrom, sync::Arc}; +use ruint::aliases::U256; +use Pairing::{G1Point, G2Point}; +use Verifier::{Proof, VerifyingKey}; #[tokio::test] async fn solidity_verifier() -> Result<()> { @@ -36,21 +33,18 @@ async fn solidity_verifier() -> Result<()> { let proof = Groth16::::prove(¶ms, circom, &mut rng)?; // launch the network & compile the verifier - let anvil = Anvil::new().spawn(); - let acc = anvil.addresses()[0]; - let provider = Provider::::try_from(anvil.endpoint())?; - let provider = provider.with_sender(acc); - let provider = Arc::new(provider); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .on_anvil_with_wallet(); // deploy the verifier - let contract = Groth16Verifier::deploy(provider.clone(), ())? - .send() - .await?; + let contract = Groth16Verifier::deploy(provider).await?; // check the proof - let verified = contract - .check_proof(proof, params.vk, inputs.as_slice()) - .await?; + let inputs: Vec = inputs.into_iter().map(|i| i.into()).collect(); + let proof: Proof = ethereum::Proof::from(proof).into(); + let vk: VerifyingKey = ethereum::VerifyingKey::from(params.vk).into(); + let verified = contract.verify(inputs, proof, vk).call().await?._0; assert!(verified); @@ -60,10 +54,15 @@ async fn solidity_verifier() -> Result<()> { // We need to implement the conversion from the Ark-Circom's internal Ethereum types to // the ones expected by the abigen'd types. Could we maybe provide a convenience // macro for these, given that there's room for implementation error? -abigen!(Groth16Verifier, "./tests/verifier_artifact.json"); +sol!( + #[sol(rpc)] + Groth16Verifier, + "./tests/verifier_artifact.json" +); + impl From for G1Point { fn from(src: ethereum::G1) -> Self { - Self { x: src.x, y: src.y } + Self { X: src.x, Y: src.y } } } impl From for G2Point { @@ -71,49 +70,26 @@ impl From for G2Point { // We should use the `.as_tuple()` method which handles converting // the G2 elements to have the second limb first let src = src.as_tuple(); - Self { x: src.0, y: src.1 } + Self { X: src.0, Y: src.1 } } } impl From for Proof { fn from(src: ethereum::Proof) -> Self { Self { - a: src.a.into(), - b: src.b.into(), - c: src.c.into(), + A: src.a.into(), + B: src.b.into(), + C: src.c.into(), } } } impl From for VerifyingKey { fn from(src: ethereum::VerifyingKey) -> Self { Self { - alfa_1: src.alpha1.into(), - beta_2: src.beta2.into(), - gamma_2: src.gamma2.into(), - delta_2: src.delta2.into(), - ic: src.ic.into_iter().map(|i| i.into()).collect(), + alfa1: src.alpha1.into(), + beta2: src.beta2.into(), + gamma2: src.gamma2.into(), + delta2: src.delta2.into(), + IC: src.ic.into_iter().map(|i| i.into()).collect(), } } } - -impl Groth16Verifier { - async fn check_proof< - I: Into, - P: Into, - VK: Into, - >( - &self, - proof: P, - vk: VK, - inputs: I, - ) -> Result> { - // convert into the expected format by the contract - let proof = proof.into().into(); - let vk = vk.into().into(); - let inputs = inputs.into().0; - - // query the contract - let res = self.verify(inputs, proof, vk).call().await?; - - Ok(res) - } -} From 49729d3de8f84a24f50e7d138cf235054ee56d91 Mon Sep 17 00:00:00 2001 From: pd <5864173+paolodamico@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:27:20 +0100 Subject: [PATCH 2/2] add alloc for ruint --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4c6c451..3610a64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ thiserror = "1.0.39" color-eyre = "0.6.2" # ethereum compat -ruint = { version = "1.12.3", default-features = false ,optional = true, features=["ark-ff-04"] } +ruint = { version = "1.12.3", default-features = false ,optional = true, features=["alloc", "ark-ff-04"] } cfg-if = "1.0.0"