arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/tests/solidity.rs
tests/solidity.rsRaw
  1use ark_circom::{ethereum, CircomBuilder, CircomConfig};
  2use ark_std::rand::thread_rng;
  3use color_eyre::Result;
  4
  5use ark_bn254::Bn254;
  6use ark_groth16::{create_random_proof as prove, generate_random_parameters};
  7
  8use ethers::{
  9    contract::ContractError,
 10    prelude::abigen,
 11    providers::{Http, Middleware, Provider},
 12    utils::Anvil,
 13};
 14use std::{convert::TryFrom, sync::Arc};
 15
 16#[tokio::test]
 17async fn solidity_verifier() -> Result<()> {
 18    let cfg = CircomConfig::<Bn254>::new(
 19        "./test-vectors/mycircuit.wasm",
 20        "./test-vectors/mycircuit.r1cs",
 21    )?;
 22    let mut builder = CircomBuilder::new(cfg);
 23    builder.push_input("a", 3);
 24    builder.push_input("b", 11);
 25
 26    // create an empty instance for setting it up
 27    let circom = builder.setup();
 28
 29    let mut rng = thread_rng();
 30    let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?;
 31
 32    let circom = builder.build()?;
 33    let inputs = circom.get_public_inputs().unwrap();
 34
 35    let proof = prove(circom, &params, &mut rng)?;
 36
 37    // launch the network & compile the verifier
 38    let anvil = Anvil::new().spawn();
 39    let acc = anvil.addresses()[0];
 40    let provider = Provider::<Http>::try_from(anvil.endpoint())?;
 41    let provider = provider.with_sender(acc);
 42    let provider = Arc::new(provider);
 43
 44    // deploy the verifier
 45    let contract = Groth16Verifier::deploy(provider.clone(), ())?
 46        .send()
 47        .await?;
 48
 49    // check the proof
 50    let verified = contract
 51        .check_proof(proof, params.vk, inputs.as_slice())
 52        .await?;
 53
 54    assert!(verified);
 55
 56    Ok(())
 57}
 58
 59// We need to implement the conversion from the Ark-Circom's internal Ethereum types to
 60// the ones expected by the abigen'd types. Could we maybe provide a convenience
 61// macro for these, given that there's room for implementation error?
 62abigen!(Groth16Verifier, "./tests/verifier_artifact.json");
 63use groth_16_verifier::{G1Point, G2Point, Proof, VerifyingKey};
 64impl From<ethereum::G1> for G1Point {
 65    fn from(src: ethereum::G1) -> Self {
 66        Self { x: src.x, y: src.y }
 67    }
 68}
 69impl From<ethereum::G2> for G2Point {
 70    fn from(src: ethereum::G2) -> Self {
 71        // We should use the `.as_tuple()` method which handles converting
 72        // the G2 elements to have the second limb first
 73        let src = src.as_tuple();
 74        Self { x: src.0, y: src.1 }
 75    }
 76}
 77impl From<ethereum::Proof> for Proof {
 78    fn from(src: ethereum::Proof) -> Self {
 79        Self {
 80            a: src.a.into(),
 81            b: src.b.into(),
 82            c: src.c.into(),
 83        }
 84    }
 85}
 86impl From<ethereum::VerifyingKey> for VerifyingKey {
 87    fn from(src: ethereum::VerifyingKey) -> Self {
 88        Self {
 89            alfa_1: src.alpha1.into(),
 90            beta_2: src.beta2.into(),
 91            gamma_2: src.gamma2.into(),
 92            delta_2: src.delta2.into(),
 93            ic: src.ic.into_iter().map(|i| i.into()).collect(),
 94        }
 95    }
 96}
 97
 98impl<M: Middleware> Groth16Verifier<M> {
 99    async fn check_proof<
100        I: Into<ethereum::Inputs>,
101        P: Into<ethereum::Proof>,
102        VK: Into<ethereum::VerifyingKey>,
103    >(
104        &self,
105        proof: P,
106        vk: VK,
107        inputs: I,
108    ) -> Result<bool, ContractError<M>> {
109        // convert into the expected format by the contract
110        let proof = proof.into().into();
111        let vk = vk.into().into();
112        let inputs = inputs.into().0;
113
114        // query the contract
115        let res = self.verify(inputs, proof, vk).call().await?;
116
117        Ok(res)
118    }
119}