Skip to main content

midnight_circuits/field/foreign/
params.rs

1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! Foreign-field arithmetic parameters for different emulation scenarios.
15//!
16//! All the emulation parameters from this file were generated using
17//! [scripts/foreign_params_gen.py].
18
19use std::{fmt::Debug, ops::Rem};
20
21#[cfg(feature = "dev-curves")]
22use midnight_curves::bn256;
23use midnight_curves::{k256, p256};
24use num_bigint::{BigInt, BigInt as BI, ToBigInt};
25use num_traits::{One, Signed};
26
27use crate::{ecc::curves::CircuitCurve, CircuitField};
28
29/// Trait for configuring a (foreign) FieldChip. These parameters need to be
30/// manually optimized for each emulation of field K over native field F.
31/// These parameters were generated with our script:
32/// `scripts/foreign_params_gen.py`.
33pub trait FieldEmulationParams<F: CircuitField, K: CircuitField>:
34    Default + Clone + Debug + PartialEq + Eq
35{
36    /// The logarithm in base 2 (bit length) of the base in which we represent
37    /// integers modulo the emulated modulus.
38    /// The actual base is 2 powered to this constant.
39    const LOG2_BASE: u32;
40
41    /// The number of limbs used to represent a emulated field element.
42    /// It must hold base^NB_LIMBS >= emulated_modulus.
43    const NB_LIMBS: u32;
44
45    /// Vector of powers of the base, used for the foreign-field identities.
46    /// The i-th element must be congruent to base^i modulo the emulated
47    /// modulus.
48    fn base_powers() -> Vec<BI> {
49        let two = BI::from(2);
50        let m = &K::modulus().to_bigint().unwrap();
51        (0..Self::NB_LIMBS)
52            .map(|i| two.pow(Self::LOG2_BASE * i).rem(m))
53            .collect::<Vec<_>>()
54    }
55
56    /// Vector of powers of the base, used for the foreign-field identities.
57    /// The (i * nb_limbs + j)-th element must be congruent to base^(i+j) modulo
58    /// the emulated modulus.
59    fn double_base_powers() -> Vec<BI> {
60        let two = BI::from(2);
61        let m = &K::modulus().to_bigint().unwrap();
62        (0..Self::NB_LIMBS)
63            .flat_map(|i| {
64                (0..Self::NB_LIMBS)
65                    .map(|j| two.pow(Self::LOG2_BASE * (i + j)).rem(m))
66                    .collect::<Vec<_>>()
67            })
68            .collect::<Vec<_>>()
69    }
70
71    /// Auxiliary moduli used in the identities. They should be as large as
72    /// possible, to maximize their contribution to the lcm bound.
73    /// On the other hand, they cannot be excessively large, in order to
74    /// guarantee no wrap-around (modulo the native modulus) in the equations.
75    fn moduli() -> Vec<BI>;
76
77    /// A bound on the maximum size of the absolute value of limb bounds for
78    /// non-well-formed emulated field elements. If such bound is exceeded, the
79    /// normalization function can no longer be applied.
80    /// We set this value to be base^2 by default. This value is guaranteed to
81    /// be supported with the same moduli as those used for the multiplication
82    /// gate. Another good choice for this value would be the largest possible
83    /// value that allows us to implement the normalization gate with only
84    /// one extra auxiliary modulus.
85    fn max_limb_bound() -> BI {
86        BI::from(2).pow(2 * Self::LOG2_BASE)
87    }
88
89    /// Log2 of the limb size of range-checks. This value is different and
90    /// independent of base, the size of ModArith limbs.
91    const RC_LIMB_SIZE: u32;
92}
93
94/// Sanity checks on the parameters for the FieldChip to be sound.
95pub(crate) fn check_params<F, K, P>()
96where
97    F: CircuitField,
98    K: CircuitField,
99    P: FieldEmulationParams<F, K>,
100{
101    let m = &K::modulus().to_bigint().unwrap();
102    let base = BI::from(2).pow(P::LOG2_BASE);
103    let nb_limbs = P::NB_LIMBS;
104
105    // The integer represented by limbs [x0, ..., x_{n-1}] is 1 + sum_i base^i xi
106
107    assert!(*m > BI::one());
108    assert!(base > BI::one());
109
110    // Assert that we can encode any integer in [Z_m] with [nb_limbs] limbs of size
111    // [base].
112    assert!(BI::pow(&base, nb_limbs) >= *m);
113
114    let base_powers = P::base_powers();
115    let double_base_powers = P::double_base_powers();
116
117    assert_eq!(base_powers.len(), nb_limbs as usize);
118    assert_eq!(double_base_powers.len(), (nb_limbs * nb_limbs) as usize);
119
120    let expected_powers = (0..nb_limbs).map(|i| BI::pow(&base, i).rem(m));
121    let expected_double_powers = (0..nb_limbs)
122        .flat_map(|i| (0..nb_limbs).map(|j| BI::pow(&base, i + j).rem(m)).collect::<Vec<_>>());
123
124    // Check that the powers in ModAP are congruent to the expected powers modulo m.
125    base_powers
126        .iter()
127        .chain(double_base_powers.iter())
128        .zip(expected_powers.chain(expected_double_powers))
129        .for_each(|(b, e)| {
130            // The assertion on the base powers being negative can be removed if we
131            // generalize the way upper-bounds are computed. ATM they are simply computed by
132            // considering the integer represented with all limbs set to (base-1).
133            assert!(!BI::is_negative(b));
134            assert_eq!(b.rem(m), e.rem(m))
135        });
136}
137
138/// MultiEmulationParams.
139#[derive(Clone, Default, Debug, PartialEq, Eq)]
140pub struct MultiEmulationParams {}
141
142/// Implement FieldEmulationParams for any curve that can emulate itself through
143/// MultiEmulationParams.
144impl<C: CircuitCurve + Default> FieldEmulationParams<C::ScalarField, C::Base> for C
145where
146    MultiEmulationParams: FieldEmulationParams<C::ScalarField, C::Base>,
147{
148    const LOG2_BASE: u32 = MultiEmulationParams::LOG2_BASE;
149
150    const NB_LIMBS: u32 = MultiEmulationParams::NB_LIMBS;
151
152    fn moduli() -> Vec<BigInt> {
153        MultiEmulationParams::moduli()
154    }
155
156    const RC_LIMB_SIZE: u32 = MultiEmulationParams::RC_LIMB_SIZE;
157}
158
159/*
160====================================================
161Emulated: Secp256k1's Base field
162
163Native fields supported:
164 - BN254's Scalar field
165 - BLS12-381's Scalar field
166====================================================
167*/
168
169/// Secp256k1's Base field over BN254's Scalar field.
170#[cfg(feature = "dev-curves")]
171impl FieldEmulationParams<bn256::Fr, k256::Fp> for MultiEmulationParams {
172    const LOG2_BASE: u32 = 64;
173    const NB_LIMBS: u32 = 4;
174    fn moduli() -> Vec<BigInt> {
175        vec![BigInt::from(2).pow(128)]
176    }
177    const RC_LIMB_SIZE: u32 = 16;
178}
179
180/// Secp256k1's Base field over BLS12-381's Scalar field.
181impl FieldEmulationParams<midnight_curves::Fq, k256::Fp> for MultiEmulationParams {
182    const LOG2_BASE: u32 = 64;
183    const NB_LIMBS: u32 = 4;
184    fn moduli() -> Vec<BigInt> {
185        vec![BigInt::from(2).pow(128)]
186    }
187    const RC_LIMB_SIZE: u32 = 16;
188}
189
190/*
191====================================================
192Emulated: Secp256k1's Scalar field
193
194Native fields supported:
195 - BN254's Scalar field
196 - BLS12-381's Scalar field
197====================================================
198*/
199
200/// Secp256k1's Scalar field over BN254's Scalar field.
201#[cfg(feature = "dev-curves")]
202impl FieldEmulationParams<bn256::Fr, k256::Fq> for MultiEmulationParams {
203    const LOG2_BASE: u32 = 52;
204    const NB_LIMBS: u32 = 5;
205    fn moduli() -> Vec<BigInt> {
206        vec![BigInt::from(2).pow(141)]
207    }
208    const RC_LIMB_SIZE: u32 = 14;
209}
210
211/// Secp256k1's Scalar field over BLS12-381's Scalar field.
212impl FieldEmulationParams<midnight_curves::Fq, k256::Fq> for MultiEmulationParams {
213    const LOG2_BASE: u32 = 64;
214    const NB_LIMBS: u32 = 4;
215    fn moduli() -> Vec<BigInt> {
216        vec![
217            BigInt::from(2).pow(118),
218            BigInt::from(2).pow(118) - BigInt::one(),
219        ]
220    }
221    const RC_LIMB_SIZE: u32 = 17;
222}
223
224/*
225====================================================
226Emulated: Secp256r1's Base field
227
228Native fields supported:
229 - BLS12-381's Scalar field
230====================================================
231*/
232
233/// Secp256r1's Base field over BLS12-381's Scalar field.
234impl FieldEmulationParams<midnight_curves::Fq, p256::Fp> for MultiEmulationParams {
235    const LOG2_BASE: u32 = 64;
236    const NB_LIMBS: u32 = 4;
237    fn moduli() -> Vec<BigInt> {
238        vec![
239            BigInt::from(2).pow(122),
240            BigInt::from(2).pow(122) - BigInt::from(507376),
241        ]
242    }
243    const RC_LIMB_SIZE: u32 = 17;
244}
245
246/*
247====================================================
248Emulated: Secp256r1's Scalar field
249
250Native fields supported:
251 - BLS12-381's Scalar field
252====================================================
253*/
254
255/// Secp256r1's Scalar field over BLS12-381's Scalar field.
256impl FieldEmulationParams<midnight_curves::Fq, p256::Fq> for MultiEmulationParams {
257    const LOG2_BASE: u32 = 64;
258    const NB_LIMBS: u32 = 4;
259    fn moduli() -> Vec<BigInt> {
260        vec![
261            BigInt::from(2).pow(118),
262            BigInt::from(2).pow(118) - BigInt::one(),
263        ]
264    }
265    const RC_LIMB_SIZE: u32 = 17;
266}
267
268/*
269====================================================
270Emulated: BLS12-381's Base field
271
272Native fields supported:
273 - BLS12-381's Scalar field
274====================================================
275*/
276
277/// BLS12-381's Base field over BLS12-381's Scalar field.
278impl FieldEmulationParams<midnight_curves::Fq, midnight_curves::Fp> for MultiEmulationParams {
279    const LOG2_BASE: u32 = 56;
280    const NB_LIMBS: u32 = 7;
281    fn moduli() -> Vec<BigInt> {
282        vec![
283            BigInt::from(2).pow(134),
284            BigInt::from(2).pow(134) - BigInt::from(1),
285        ]
286    }
287    const RC_LIMB_SIZE: u32 = 15;
288}
289
290/*
291====================================================
292Emulated: BN254's Base field
293
294Native fields supported:
295 - BLS12-381's Scalar field
296 - BN254's Scalar field
297====================================================
298*/
299
300/// BN254's Base field over BLS12-381's Scalar field.
301#[cfg(feature = "dev-curves")]
302impl FieldEmulationParams<midnight_curves::Fq, bn256::Fq> for MultiEmulationParams {
303    const LOG2_BASE: u32 = 52;
304    const NB_LIMBS: u32 = 5;
305    fn moduli() -> Vec<BigInt> {
306        vec![BigInt::from(2).pow(142)]
307    }
308    const RC_LIMB_SIZE: u32 = 14;
309}
310
311/// BN254's Base field over BN254's Scalar field.
312#[cfg(feature = "dev-curves")]
313impl FieldEmulationParams<bn256::Fr, bn256::Fq> for MultiEmulationParams {
314    const LOG2_BASE: u32 = 52;
315    const NB_LIMBS: u32 = 5;
316    fn moduli() -> Vec<BigInt> {
317        vec![BigInt::from(2).pow(141)]
318    }
319    const RC_LIMB_SIZE: u32 = 14;
320}
321
322/*
323====================================================
324Emulated: BLS12-381's Scalar field
325
326Native fields supported:
327 - BLS12-381's Scalar field (for tests only)
328====================================================
329*/
330
331/// BLS12-381's Scalar field over BLS12-381's Scalar field.
332impl FieldEmulationParams<midnight_curves::Fq, midnight_curves::Fq> for MultiEmulationParams {
333    const LOG2_BASE: u32 = 52;
334    const NB_LIMBS: u32 = 5;
335    fn moduli() -> Vec<BigInt> {
336        vec![BigInt::from(2).pow(142)]
337    }
338    const RC_LIMB_SIZE: u32 = 14;
339}
340
341/*
342====================================================
343Emulated: Curve25519's Base field
344
345Native fields supported:
346 - BLS12-381's Scalar field
347====================================================
348*/
349
350/// Curve25519's Base field over BLS12-381's Scalar field.
351impl FieldEmulationParams<midnight_curves::Fq, midnight_curves::curve25519::Fp>
352    for MultiEmulationParams
353{
354    const LOG2_BASE: u32 = 64;
355    const NB_LIMBS: u32 = 4;
356    fn moduli() -> Vec<BigInt> {
357        vec![BigInt::from(2).pow(128)]
358    }
359    const RC_LIMB_SIZE: u32 = 16;
360}
361
362/*
363====================================================
364Emulated: Curve25519's Scalar field
365
366Native fields supported:
367 - BLS12-381's Scalar field
368====================================================
369*/
370
371/// Curve25519's Scalar field over BLS12-381's Scalar field.
372impl FieldEmulationParams<midnight_curves::Fq, midnight_curves::curve25519::Scalar>
373    for MultiEmulationParams
374{
375    const LOG2_BASE: u32 = 51;
376    const NB_LIMBS: u32 = 5;
377    fn moduli() -> Vec<BigInt> {
378        vec![BigInt::from(2).pow(146)]
379    }
380    const RC_LIMB_SIZE: u32 = 17;
381}