Skip to main content

hotshot_contract_adapter/
jellyfish.rs

1//! Helpers for connecting types between Jellyfish and Solidity.
2//! Usually used during differential testing (via FFI).
3
4use alloy::{
5    hex::ToHexExt,
6    primitives::{B256, U256},
7};
8use ark_bn254::{Bn254, Fq, Fr, G1Affine, G2Affine};
9use ark_ff::{Fp2, MontFp};
10use ark_std::{UniformRand, rand::Rng};
11use jf_pcs::prelude::Commitment;
12use jf_plonk::{
13    constants::KECCAK256_STATE_SIZE,
14    proof_system::structs::{OpenKey, Proof, ProofEvaluations, VerifyingKey},
15    testing_apis::Challenges,
16    transcript::SolidityTranscript,
17};
18use jf_plonk_compat::proof_system::structs::{
19    Proof as ProofCompat, VerifyingKey as VerifyingKeyCompat,
20};
21use jf_utils::to_bytes;
22use num_bigint::BigUint;
23use num_traits::Num;
24
25use crate::{field_to_u256, sol_types::*, u256_to_field};
26
27// constant in hex string copied from hardcoded constants from solidity contracts
28
29const COSET: [&str; 5] = [
30    "1",
31    "2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a",
32    "1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb025",
33    "2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a",
34    "2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881",
35];
36
37// H: G2Affine(x: Fp2, y:Fp2), x = x0 + u * x1, y = y0 + u * y1
38const H: [&str; 4] = [
39    "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", // x0
40    "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", // x1
41    "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", // y0
42    "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", // y1
43];
44
45// See notes about `const H` above.
46const BETA_H: [&str; 4] = [
47    "0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0",
48    "260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1",
49    "22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55",
50    "04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4",
51];
52
53// TODO: (alex) change to simply using `MontFp!("0x..")` after
54// <https://github.com/arkworks-rs/algebra/pull/635> is on a tag release
55// Return cosets coefficients for circuits over BN254.
56pub fn coset_k() -> Vec<Fr> {
57    vec![
58        Fr::from(BigUint::from_str_radix(COSET[0], 16).unwrap()),
59        Fr::from(BigUint::from_str_radix(COSET[1], 16).unwrap()),
60        Fr::from(BigUint::from_str_radix(COSET[2], 16).unwrap()),
61        Fr::from(BigUint::from_str_radix(COSET[3], 16).unwrap()),
62        Fr::from(BigUint::from_str_radix(COSET[4], 16).unwrap()),
63    ]
64}
65
66/// Returns `OpenKeys` for KZG10 over BN254 curve from Aztec's SRS
67pub fn open_key() -> OpenKey<Bn254> {
68    let g = G1Affine::new_unchecked(MontFp!("1"), MontFp!("2"));
69    let h = G2Affine::new(
70        Fp2::new(
71            Fq::from(BigUint::from_str_radix(H[0], 16).unwrap()),
72            Fq::from(BigUint::from_str_radix(H[1], 16).unwrap()),
73        ),
74        Fp2::new(
75            Fq::from(BigUint::from_str_radix(H[2], 16).unwrap()),
76            Fq::from(BigUint::from_str_radix(H[3], 16).unwrap()),
77        ),
78    );
79    let beta_h = G2Affine::new(
80        Fp2::new(
81            Fq::from(BigUint::from_str_radix(BETA_H[0], 16).unwrap()),
82            Fq::from(BigUint::from_str_radix(BETA_H[1], 16).unwrap()),
83        ),
84        Fp2::new(
85            Fq::from(BigUint::from_str_radix(BETA_H[2], 16).unwrap()),
86            Fq::from(BigUint::from_str_radix(BETA_H[3], 16).unwrap()),
87        ),
88    );
89
90    OpenKey {
91        g,
92        h,
93        beta_h,
94        powers_of_g: vec![g],
95        powers_of_h: vec![h, beta_h],
96    }
97}
98
99impl From<SolidityTranscript> for TranscriptDataSol {
100    fn from(t: SolidityTranscript) -> Self {
101        let (state, transcript) = t.internal();
102        Self {
103            state: B256::from_slice(&state),
104            transcript: transcript.into(),
105        }
106    }
107}
108
109impl From<TranscriptDataSol> for SolidityTranscript {
110    fn from(t: TranscriptDataSol) -> Self {
111        let mut state = [0u8; KECCAK256_STATE_SIZE];
112        state.copy_from_slice(&t.state.0);
113        Self::from_internal(state, t.transcript.to_vec())
114    }
115}
116
117impl From<VerifyingKey<Bn254>> for VerifyingKeySol {
118    fn from(vk: VerifyingKey<Bn254>) -> Self {
119        let g2_bytes = to_bytes!(&vk.open_key.powers_of_h[1]).unwrap();
120        assert!(g2_bytes.len() == 64);
121        let mut g2_lsb = [0u8; 32];
122        let mut g2_msb = [0u8; 32];
123        g2_lsb.copy_from_slice(&g2_bytes[..32]);
124        g2_msb.copy_from_slice(&g2_bytes[32..]);
125
126        // since G2 point from the Aztec's SRS we use is fixed
127        // remove these sanity check if using other SRS
128        // generated via:
129        // ```rust
130        // let srs = ark_srs::kzg10::aztec20::setup(2u64.pow(6) as usize + 2).expect("Aztec SRS fail to load");
131        // println!("{}", hex::encode(jf_utils::to_bytes!(&srs.beta_h).unwrap()));
132        // ```
133        assert_eq!(
134            g2_lsb.encode_hex(),
135            String::from("b0838893ec1f237e8b07323b0744599f4e97b598b3b589bcc2bc37b8d5c41801")
136        );
137        assert_eq!(
138            g2_msb.encode_hex(),
139            String::from("c18393c0fa30fe4e8b038e357ad851eae8de9107584effe7c7f1f651b2010e26")
140        );
141
142        Self {
143            domainSize: U256::from(vk.domain_size),
144            numInputs: U256::from(vk.num_inputs),
145            sigma0: vk.sigma_comms[0].0.into(),
146            sigma1: vk.sigma_comms[1].0.into(),
147            sigma2: vk.sigma_comms[2].0.into(),
148            sigma3: vk.sigma_comms[3].0.into(),
149            sigma4: vk.sigma_comms[4].0.into(),
150            q1: vk.selector_comms[0].0.into(),
151            q2: vk.selector_comms[1].0.into(),
152            q3: vk.selector_comms[2].0.into(),
153            q4: vk.selector_comms[3].0.into(),
154            qM12: vk.selector_comms[4].0.into(),
155            qM34: vk.selector_comms[5].0.into(),
156            qH1: vk.selector_comms[6].0.into(),
157            qH2: vk.selector_comms[7].0.into(),
158            qH3: vk.selector_comms[8].0.into(),
159            qH4: vk.selector_comms[9].0.into(),
160            qO: vk.selector_comms[10].0.into(),
161            qC: vk.selector_comms[11].0.into(),
162            qEcc: vk.selector_comms[12].0.into(),
163            g2LSB: g2_lsb.into(),
164            g2MSB: g2_msb.into(),
165        }
166    }
167}
168
169impl From<VerifyingKeySol> for VerifyingKey<Bn254> {
170    fn from(vk: VerifyingKeySol) -> Self {
171        let sigma_comms = vec![
172            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.sigma0)),
173            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.sigma1)),
174            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.sigma2)),
175            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.sigma3)),
176            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.sigma4)),
177        ];
178
179        let selector_comms = vec![
180            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.q1)),
181            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.q2)),
182            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.q3)),
183            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.q4)),
184            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qM12)),
185            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qM34)),
186            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qH1)),
187            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qH2)),
188            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qH3)),
189            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qH4)),
190            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qO)),
191            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qC)),
192            Commitment::from(<G1PointSol as Into<G1Affine>>::into(vk.qEcc)),
193        ];
194
195        Self {
196            domain_size: vk.domainSize.to::<usize>(),
197            num_inputs: vk.numInputs.to::<usize>(),
198            sigma_comms,
199            selector_comms,
200            k: coset_k(),
201            open_key: open_key(),
202            is_merged: false,
203            plookup_vk: None,
204        }
205    }
206}
207
208impl From<Proof<Bn254>> for PlonkProofSol {
209    fn from(proof: Proof<Bn254>) -> Self {
210        Self {
211            wire0: proof.wires_poly_comms[0].0.into(),
212            wire1: proof.wires_poly_comms[1].0.into(),
213            wire2: proof.wires_poly_comms[2].0.into(),
214            wire3: proof.wires_poly_comms[3].0.into(),
215            wire4: proof.wires_poly_comms[4].0.into(),
216            prodPerm: proof.prod_perm_poly_comm.0.into(),
217            split0: proof.split_quot_poly_comms[0].0.into(),
218            split1: proof.split_quot_poly_comms[1].0.into(),
219            split2: proof.split_quot_poly_comms[2].0.into(),
220            split3: proof.split_quot_poly_comms[3].0.into(),
221            split4: proof.split_quot_poly_comms[4].0.into(),
222            zeta: proof.opening_proof.0.into(),
223            zetaOmega: proof.shifted_opening_proof.0.into(),
224            wireEval0: field_to_u256(proof.poly_evals.wires_evals[0]),
225            wireEval1: field_to_u256(proof.poly_evals.wires_evals[1]),
226            wireEval2: field_to_u256(proof.poly_evals.wires_evals[2]),
227            wireEval3: field_to_u256(proof.poly_evals.wires_evals[3]),
228            wireEval4: field_to_u256(proof.poly_evals.wires_evals[4]),
229            sigmaEval0: field_to_u256(proof.poly_evals.wire_sigma_evals[0]),
230            sigmaEval1: field_to_u256(proof.poly_evals.wire_sigma_evals[1]),
231            sigmaEval2: field_to_u256(proof.poly_evals.wire_sigma_evals[2]),
232            sigmaEval3: field_to_u256(proof.poly_evals.wire_sigma_evals[3]),
233            prodPermZetaOmegaEval: field_to_u256(proof.poly_evals.perm_next_eval),
234        }
235    }
236}
237
238impl From<PlonkProofSol> for Proof<Bn254> {
239    fn from(proof: PlonkProofSol) -> Self {
240        let wires_poly_comms = vec![
241            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.wire0)),
242            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.wire1)),
243            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.wire2)),
244            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.wire3)),
245            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.wire4)),
246        ];
247        let split_quot_poly_comms = vec![
248            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.split0)),
249            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.split1)),
250            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.split2)),
251            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.split3)),
252            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.split4)),
253        ];
254        let prod_perm_poly_comm =
255            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.prodPerm));
256        let opening_proof = Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.zeta));
257        let shifted_opening_proof =
258            Commitment::from(<G1PointSol as Into<G1Affine>>::into(proof.zetaOmega));
259
260        let wires_evals = vec![
261            u256_to_field(proof.wireEval0),
262            u256_to_field(proof.wireEval1),
263            u256_to_field(proof.wireEval2),
264            u256_to_field(proof.wireEval3),
265            u256_to_field(proof.wireEval4),
266        ];
267        let wire_sigma_evals = vec![
268            u256_to_field(proof.sigmaEval0),
269            u256_to_field(proof.sigmaEval1),
270            u256_to_field(proof.sigmaEval2),
271            u256_to_field(proof.sigmaEval3),
272        ];
273        let perm_next_eval = u256_to_field(proof.prodPermZetaOmegaEval);
274
275        Self {
276            wires_poly_comms,
277            prod_perm_poly_comm,
278            split_quot_poly_comms,
279            opening_proof,
280            shifted_opening_proof,
281            poly_evals: ProofEvaluations {
282                wires_evals,
283                wire_sigma_evals,
284                perm_next_eval,
285            },
286            plookup_proof: None,
287        }
288    }
289}
290
291impl PlonkProofSol {
292    /// return a dummy proof instance with random ProofEvaluations fields.
293    pub fn dummy_with_rand_proof_evals<R: Rng>(rng: &mut R) -> Self {
294        let zero = G1Affine::default();
295        Self {
296            wire0: zero.into(),
297            wire1: zero.into(),
298            wire2: zero.into(),
299            wire3: zero.into(),
300            wire4: zero.into(),
301            prodPerm: zero.into(),
302            split0: zero.into(),
303            split1: zero.into(),
304            split2: zero.into(),
305            split3: zero.into(),
306            split4: zero.into(),
307            zeta: zero.into(),
308            zetaOmega: zero.into(),
309            wireEval0: field_to_u256(Fr::rand(rng)),
310            wireEval1: field_to_u256(Fr::rand(rng)),
311            wireEval2: field_to_u256(Fr::rand(rng)),
312            wireEval3: field_to_u256(Fr::rand(rng)),
313            wireEval4: field_to_u256(Fr::rand(rng)),
314            sigmaEval0: field_to_u256(Fr::rand(rng)),
315            sigmaEval1: field_to_u256(Fr::rand(rng)),
316            sigmaEval2: field_to_u256(Fr::rand(rng)),
317            sigmaEval3: field_to_u256(Fr::rand(rng)),
318            prodPermZetaOmegaEval: field_to_u256(Fr::rand(rng)),
319        }
320    }
321
322    /// return a dummy proof instance with all random fields
323    pub fn dummy<R: Rng>(rng: &mut R) -> Self {
324        let mut proof = Self::dummy_with_rand_proof_evals(rng);
325        proof.wire0 = G1Affine::rand(rng).into();
326        proof.wire1 = G1Affine::rand(rng).into();
327        proof.wire2 = G1Affine::rand(rng).into();
328        proof.wire3 = G1Affine::rand(rng).into();
329        proof.wire4 = G1Affine::rand(rng).into();
330        proof.prodPerm = G1Affine::rand(rng).into();
331        proof.split0 = G1Affine::rand(rng).into();
332        proof.split1 = G1Affine::rand(rng).into();
333        proof.split2 = G1Affine::rand(rng).into();
334        proof.split3 = G1Affine::rand(rng).into();
335        proof.split4 = G1Affine::rand(rng).into();
336        proof.zeta = G1Affine::rand(rng).into();
337        proof.zetaOmega = G1Affine::rand(rng).into();
338        proof
339    }
340}
341
342impl From<Challenges<Fr>> for ChallengesSol {
343    fn from(c: Challenges<Fr>) -> Self {
344        let alpha_2 = c.alpha * c.alpha;
345        Self {
346            alpha: field_to_u256::<Fr>(c.alpha),
347            alpha2: field_to_u256::<Fr>(alpha_2),
348            alpha3: field_to_u256::<Fr>(c.alpha * alpha_2),
349            beta: field_to_u256::<Fr>(c.beta),
350            gamma: field_to_u256::<Fr>(c.gamma),
351            zeta: field_to_u256::<Fr>(c.zeta),
352            v: field_to_u256::<Fr>(c.v),
353            u: field_to_u256::<Fr>(c.u),
354        }
355    }
356}
357
358impl From<ChallengesSol> for Challenges<Fr> {
359    fn from(c: ChallengesSol) -> Self {
360        Self {
361            tau: None,
362            alpha: u256_to_field(c.alpha),
363            beta: u256_to_field(c.beta),
364            gamma: u256_to_field(c.gamma),
365            zeta: u256_to_field(c.zeta),
366            v: u256_to_field(c.v),
367            u: u256_to_field(c.u),
368        }
369    }
370}
371
372impl ChallengesSol {
373    /// dummy challenges
374    #[allow(dead_code)]
375    pub fn dummy<R: Rng>(rng: &mut R) -> Self {
376        let alpha = Fr::rand(rng);
377        let alpha_2 = alpha * alpha;
378        let alpha_3 = alpha * alpha_2;
379        Self {
380            alpha: field_to_u256(alpha),
381            alpha2: field_to_u256(alpha_2),
382            alpha3: field_to_u256(alpha_3),
383            beta: field_to_u256(Fr::rand(rng)),
384            gamma: field_to_u256(Fr::rand(rng)),
385            zeta: field_to_u256(Fr::rand(rng)),
386            v: field_to_u256(Fr::rand(rng)),
387            u: field_to_u256(Fr::rand(rng)),
388        }
389    }
390}
391
392impl From<VerifyingKeyCompat<Bn254>> for VerifyingKeySol {
393    fn from(vk: VerifyingKeyCompat<Bn254>) -> Self {
394        let vk: VerifyingKey<Bn254> = unsafe { std::mem::transmute(vk) };
395        vk.into()
396    }
397}
398
399impl From<VerifyingKeySol> for VerifyingKeyCompat<Bn254> {
400    fn from(vk: VerifyingKeySol) -> Self {
401        let vk: VerifyingKey<Bn254> = vk.into();
402        unsafe { std::mem::transmute(vk) }
403    }
404}
405
406impl From<ProofCompat<Bn254>> for PlonkProofSol {
407    fn from(proof: ProofCompat<Bn254>) -> Self {
408        let proof: Proof<Bn254> = unsafe { std::mem::transmute(proof) };
409        proof.into()
410    }
411}
412
413impl From<PlonkProofSol> for ProofCompat<Bn254> {
414    fn from(proof: PlonkProofSol) -> Self {
415        let proof: Proof<Bn254> = proof.into();
416        unsafe { std::mem::transmute(proof) }
417    }
418}