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, Fr};
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::<Fr>::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(¶ms, 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}