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, ¶ms, &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}