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