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::{abigen, ContractError, ContractFactory},
 10    providers::{Http, Middleware, Provider},
 11    utils::{compile_and_launch_ganache, Ganache, Solc},
 12};
 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 (compiled, ganache) =
 39        compile_and_launch_ganache(Solc::new("./tests/verifier.sol"), Ganache::new()).await?;
 40    let acc = ganache.addresses()[0];
 41    let provider = Provider::<Http>::try_from(ganache.endpoint())?;
 42    let provider = provider.with_sender(acc);
 43    let provider = Arc::new(provider);
 44
 45    // deploy the verifier
 46    let contract = {
 47        let contract = compiled
 48            .get("TestVerifier")
 49            .expect("could not find contract");
 50
 51        let factory = ContractFactory::new(
 52            contract.abi.clone(),
 53            contract.bytecode.clone(),
 54            provider.clone(),
 55        );
 56        let contract = factory.deploy(())?.send().await?;
 57        let addr = contract.address();
 58        Groth16Verifier::new(addr, provider)
 59    };
 60
 61    // check the proof
 62    let verified = contract
 63        .check_proof(proof, params.vk, inputs.as_slice())
 64        .await?;
 65    assert!(verified);
 66
 67    Ok(())
 68}
 69
 70// We need to implement the conversion from the Ark-Circom's internal Ethereum types to
 71// the ones expected by the abigen'd types. Could we maybe provide a convenience
 72// macro for these, given that there's room for implementation error?
 73abigen!(Groth16Verifier, "./tests/verifier_abi.json");
 74use groth16verifier_mod::{G1Point, G2Point, Proof, VerifyingKey};
 75impl From<ethereum::G1> for G1Point {
 76    fn from(src: ethereum::G1) -> Self {
 77        Self { x: src.x, y: src.y }
 78    }
 79}
 80impl From<ethereum::G2> for G2Point {
 81    fn from(src: ethereum::G2) -> Self {
 82        // We should use the `.as_tuple()` method which handles converting
 83        // the G2 elements to have the second limb first
 84        let src = src.as_tuple();
 85        Self { x: src.0, y: src.1 }
 86    }
 87}
 88impl From<ethereum::Proof> for Proof {
 89    fn from(src: ethereum::Proof) -> Self {
 90        Self {
 91            a: src.a.into(),
 92            b: src.b.into(),
 93            c: src.c.into(),
 94        }
 95    }
 96}
 97impl From<ethereum::VerifyingKey> for VerifyingKey {
 98    fn from(src: ethereum::VerifyingKey) -> Self {
 99        Self {
100            alfa_1: src.alpha1.into(),
101            beta_2: src.beta2.into(),
102            gamma_2: src.gamma2.into(),
103            delta_2: src.delta2.into(),
104            ic: src.ic.into_iter().map(|i| i.into()).collect(),
105        }
106    }
107}
108
109impl<M: Middleware> Groth16Verifier<M> {
110    async fn check_proof<
111        I: Into<ethereum::Inputs>,
112        P: Into<ethereum::Proof>,
113        VK: Into<ethereum::VerifyingKey>,
114    >(
115        &self,
116        proof: P,
117        vk: VK,
118        inputs: I,
119    ) -> Result<bool, ContractError<M>> {
120        // convert into the expected format by the contract
121        let proof = proof.into().into();
122        let vk = vk.into().into();
123        let inputs = inputs.into().0;
124
125        // query the contract
126        let res = self.verify(inputs, proof, vk).call().await?;
127
128        Ok(res)
129    }
130}