sigmabus-poc/src/sigmabus.rs
src/sigmabus.rsRaw
1use ark_crypto_primitives::{
2 crh::{poseidon::CRH, CRHScheme},
3 snark::SNARK,
4 sponge::{poseidon::PoseidonConfig, Absorb},
5};
6use ark_ec::{pairing::Pairing, CurveGroup, Group};
7use ark_groth16::{Groth16, Proof as Groth16Proof};
8use ark_std::{
9 rand::{CryptoRng, Rng},
10 UniformRand, Zero,
11};
12use std::marker::PhantomData;
13use std::ops::Mul;
14
15use crate::circuits::GenZKCircuit;
16use crate::transcript::PoseidonTranscript;
17use crate::Error;
18
19/// Proof represents the Sigmabus proof
20pub struct Proof<E: Pairing> {
21 cm: E::ScalarField,
22 sigma_proof: SigmaProof<E::G1>,
23 zkproof: Groth16Proof<E>,
24}
25
26/// SigmaProof represents the Sigma protocol proof (not Sigmabus proof)
27pub struct SigmaProof<C: CurveGroup> {
28 pub s: C::ScalarField,
29 pub R: C,
30 pub r_h: C::ScalarField,
31}
32
33pub struct Params<E: Pairing> {
34 _e: PhantomData<E>,
35 poseidon_config: PoseidonConfig<E::ScalarField>,
36 pk: <Groth16<E> as SNARK<E::ScalarField>>::ProvingKey,
37 vk: <Groth16<E> as SNARK<E::ScalarField>>::VerifyingKey,
38}
39
40/// Sigmabus implements [Sigmabus](https://eprint.iacr.org/2023/1406) prover & verifier for proving
41/// X=x*G as described in section 3 of the paper, using Groth16's zkSNARK scheme.
42pub struct Sigmabus<E: Pairing> {
43 _e: PhantomData<E>,
44}
45
46impl<E: Pairing> Sigmabus<E>
47where
48 E::ScalarField: Absorb,
49{
50 pub fn setup<R: Rng + CryptoRng>(
51 rng: &mut R,
52 poseidon_config: &PoseidonConfig<E::ScalarField>,
53 ) -> Params<E> {
54 let circuit = GenZKCircuit::<E::G1> {
55 _c: PhantomData,
56 poseidon_config: poseidon_config.clone(),
57 // public
58 cm: E::ScalarField::zero(),
59 s: E::ScalarField::zero(),
60 r_h: E::ScalarField::zero(),
61 c: E::ScalarField::zero(),
62 // private
63 x: E::ScalarField::zero(),
64 r: E::ScalarField::zero(),
65 o_h: E::ScalarField::zero(),
66 };
67
68 // generate the snark proof
69 let (pk, vk) = Groth16::<E>::circuit_specific_setup(circuit.clone(), rng).unwrap();
70 Params::<E> {
71 _e: PhantomData,
72 poseidon_config: poseidon_config.clone(),
73 pk,
74 vk,
75 }
76 }
77
78 pub fn prove<R: Rng + CryptoRng>(
79 rng: &mut R,
80 params: &Params<E>,
81 transcript: &mut PoseidonTranscript<E::G1>,
82 x: E::ScalarField,
83 ) -> Result<Proof<E>, Error> {
84 // cm
85 let cm: E::ScalarField =
86 CRH::<E::ScalarField>::evaluate(¶ms.poseidon_config, [x]).unwrap();
87 transcript.absorb(&cm);
88
89 let r = E::ScalarField::rand(rng);
90 let o_h = E::ScalarField::rand(rng);
91
92 let R = E::G1::generator().mul(r);
93
94 let r_h: E::ScalarField =
95 CRH::<E::ScalarField>::evaluate(¶ms.poseidon_config, [r, o_h]).unwrap();
96
97 transcript.absorb_point(&R);
98 transcript.absorb(&r_h);
99
100 let c = transcript.get_challenge();
101
102 let s = r + c * x;
103
104 let circuit = GenZKCircuit::<E::G1> {
105 _c: PhantomData,
106 poseidon_config: params.poseidon_config.clone(),
107 // public
108 cm,
109 s,
110 r_h,
111 c,
112 // private
113 x,
114 r,
115 o_h,
116 };
117
118 // generate the snark proof
119 let zkproof = Groth16::<E>::prove(¶ms.pk, circuit.clone(), rng).unwrap();
120
121 Ok(Proof {
122 cm,
123 sigma_proof: SigmaProof { s, R, r_h },
124 zkproof,
125 })
126 }
127
128 pub fn verify(
129 params: &Params<E>,
130 transcript: &mut PoseidonTranscript<E::G1>,
131 proof: Proof<E>,
132 X: E::G1,
133 ) -> Result<(), Error> {
134 let lhs = E::G1::generator().mul(proof.sigma_proof.s);
135
136 transcript.absorb(&proof.cm);
137 transcript.absorb_point(&proof.sigma_proof.R);
138 transcript.absorb(&proof.sigma_proof.r_h);
139 let c = transcript.get_challenge();
140
141 let rhs = proof.sigma_proof.R + X.mul(c);
142
143 if lhs != rhs {
144 return Err(Error::SigmaFail);
145 }
146
147 // verify zkSNARK proof
148 let public_input = [proof.cm, proof.sigma_proof.s, proof.sigma_proof.r_h, c];
149
150 let valid_proof = Groth16::<E>::verify(¶ms.vk, &public_input, &proof.zkproof).unwrap();
151 if !valid_proof {
152 return Err(Error::GenZKFail);
153 }
154
155 Ok(())
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use ark_bn254::{Bn254, Fr, G1Projective};
163 use ark_std::rand::{RngCore, SeedableRng};
164 use ark_std::test_rng;
165
166 use crate::transcript::tests::poseidon_test_config;
167
168 #[test]
169 fn test_sigmabus_prove_verify() {
170 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
171 let poseidon_config = poseidon_test_config::<Fr>();
172
173 // generate the trusted setup
174 let params = Sigmabus::<Bn254>::setup(&mut rng, &poseidon_config);
175
176 // compute X = x * G
177 let x = Fr::rand(&mut rng);
178 let X = G1Projective::generator().mul(x);
179
180 let mut transcript_p = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
181
182 // generate Sigmabus proof for X==x*G
183 let proof = Sigmabus::<Bn254>::prove(&mut rng, ¶ms, &mut transcript_p, x).unwrap();
184
185 // verify Sigmabus proof for X==x*G
186 let mut transcript_v = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
187 Sigmabus::<Bn254>::verify(¶ms, &mut transcript_v, proof, X).unwrap();
188 }
189}