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