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