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::::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::::generate_random_parameters_with_reduction(circom, &mut rng)?; let circom = builder.build()?; let inputs = circom.get_public_inputs().unwrap(); 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); // 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"); impl From for G1Point { fn from(src: ethereum::G1) -> Self { Self { x: src.x, y: src.y } } } impl From 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 for Proof { fn from(src: ethereum::Proof) -> Self { Self { 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(), } } } 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) } }