mirror of
https://github.com/arnaucube/circom-compat.git
synced 2026-01-29 06:56:42 +01:00
The motivation to do so, is so we can use the witness generation with other curves that don't have pairings (and hence the Pairing trait implemented).
121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
use ark_circom::{ethereum, CircomBuilder, CircomConfig};
|
|
use ark_std::rand::thread_rng;
|
|
use color_eyre::Result;
|
|
|
|
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};
|
|
|
|
#[tokio::test]
|
|
async fn solidity_verifier() -> Result<()> {
|
|
let cfg = CircomConfig::<Fr>::new(
|
|
"./test-vectors/mycircuit.wasm",
|
|
"./test-vectors/mycircuit.r1cs",
|
|
)?;
|
|
let mut builder = CircomBuilder::new(cfg);
|
|
builder.push_input("a", 3);
|
|
builder.push_input("b", 11);
|
|
|
|
// create an empty instance for setting it up
|
|
let circom = builder.setup();
|
|
|
|
let mut rng = thread_rng();
|
|
let params = Groth16::<Bn254>::generate_random_parameters_with_reduction(circom, &mut rng)?;
|
|
|
|
let circom = builder.build()?;
|
|
let inputs = circom.get_public_inputs().unwrap();
|
|
|
|
let proof = Groth16::<Bn254>::prove(¶ms, circom, &mut rng)?;
|
|
|
|
// launch the network & compile the verifier
|
|
let anvil = Anvil::new().spawn();
|
|
let acc = anvil.addresses()[0];
|
|
let provider = Provider::<Http>::try_from(anvil.endpoint())?;
|
|
let provider = provider.with_sender(acc);
|
|
let provider = Arc::new(provider);
|
|
|
|
// deploy the verifier
|
|
let contract = Groth16Verifier::deploy(provider.clone(), ())?
|
|
.send()
|
|
.await?;
|
|
|
|
// check the proof
|
|
let verified = contract
|
|
.check_proof(proof, params.vk, inputs.as_slice())
|
|
.await?;
|
|
|
|
assert!(verified);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// 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");
|
|
use groth_16_verifier::{G1Point, G2Point, Proof, VerifyingKey};
|
|
impl From<ethereum::G1> for G1Point {
|
|
fn from(src: ethereum::G1) -> Self {
|
|
Self { x: src.x, y: src.y }
|
|
}
|
|
}
|
|
impl From<ethereum::G2> for G2Point {
|
|
fn from(src: ethereum::G2) -> Self {
|
|
// 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 }
|
|
}
|
|
}
|
|
impl From<ethereum::Proof> for Proof {
|
|
fn from(src: ethereum::Proof) -> Self {
|
|
Self {
|
|
a: src.a.into(),
|
|
b: src.b.into(),
|
|
c: src.c.into(),
|
|
}
|
|
}
|
|
}
|
|
impl From<ethereum::VerifyingKey> 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(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<M: Middleware> Groth16Verifier<M> {
|
|
async fn check_proof<
|
|
I: Into<ethereum::Inputs>,
|
|
P: Into<ethereum::Proof>,
|
|
VK: Into<ethereum::VerifyingKey>,
|
|
>(
|
|
&self,
|
|
proof: P,
|
|
vk: VK,
|
|
inputs: I,
|
|
) -> Result<bool, ContractError<M>> {
|
|
// 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)
|
|
}
|
|
}
|