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