mithril_stm/circuits/halo2/off_circuit/
utils.rs

1use ff::{Field, PrimeField};
2use num_bigint::BigUint;
3use num_integer::Integer;
4use num_traits::{Num, One, Zero};
5use subtle::{Choice, ConstantTimeEq};
6
7use crate::circuits::halo2::constants::EDWARDS_D;
8use crate::circuits::halo2::types::{
9    Jubjub, JubjubAffine, JubjubBase, JubjubScalar, JubjubSubgroup,
10};
11
12pub fn get_coordinates(point: JubjubSubgroup) -> (JubjubBase, JubjubBase) {
13    let extended: Jubjub = point.into(); // Convert to JubjubExtended
14    let affine: JubjubAffine = extended.into(); // Convert to JubjubAffine (affine coordinates)
15    let x = affine.get_u(); // Get x-coordinate
16    let y = affine.get_v(); // Get y-coordinate
17    (x, y)
18}
19
20pub fn jubjub_base_to_scalar(x: JubjubBase) -> JubjubScalar {
21    let bytes = x.to_bytes_le();
22    JubjubScalar::from_raw([
23        u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
24        u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
25        u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
26        u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
27    ])
28}
29
30pub fn jubjub_base_from_le_bytes(bytes: &[u8]) -> JubjubBase {
31    assert_eq!(bytes.len(), 32);
32    JubjubBase::from_raw([
33        u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
34        u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
35        u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
36        u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
37    ])
38}
39
40pub fn is_on_curve(u: JubjubBase, v: JubjubBase) -> Choice {
41    let u2 = u.square();
42    let v2 = v.square();
43
44    // Left-hand side: v² - u²
45    let lhs = v2 - u2;
46
47    // Right-hand side: 1 + EDWARDS_D * (u² * v²)
48    let rhs = JubjubBase::ONE + EDWARDS_D * u2 * v2;
49
50    // Compare in constant time
51    lhs.ct_eq(&rhs)
52}
53
54/// Breaks the given `value` into `nb_limbs` limbs representing the value in the
55/// given `base` (in little-endian).
56/// Panics if the conversion is not possible.
57pub fn big_to_limbs(nb_limbs: u32, base: &BigUint, value: &BigUint) -> Vec<BigUint> {
58    let mut output = vec![];
59    let mut q = (*value).clone();
60    let mut r;
61    while output.len() < nb_limbs as usize {
62        (q, r) = q.div_rem(base);
63        output.push(r.clone());
64    }
65    if !BigUint::is_zero(&q) {
66        panic!(
67            "big_to_limbs: {} cannot be expressed in base {} with {} limbs",
68            value, base, nb_limbs
69        )
70    };
71    output
72}
73
74pub fn modulus<F: PrimeField>() -> BigUint {
75    BigUint::from_str_radix(&F::MODULUS[2..], 16).unwrap()
76}
77
78pub fn big_to_fe<F: PrimeField>(e: BigUint) -> F {
79    let modulus = modulus::<F>();
80    let e = e % modulus;
81    F::from_str_vartime(&e.to_str_radix(10)[..]).unwrap()
82}
83
84pub fn fe_to_big<F: PrimeField>(fe: F) -> BigUint {
85    BigUint::from_bytes_le(fe.to_repr().as_ref())
86}
87
88pub fn decompose<F: PrimeField>(value: &F, limb_bits: usize) -> Vec<F> {
89    let value_big = BigUint::from_bytes_le(value.to_repr().as_ref());
90    let nb_limbs = value_big.bits().div_ceil(limb_bits as u64) as u32;
91    big_to_limbs(nb_limbs, &(BigUint::from(1u8) << limb_bits), &value_big)
92        .into_iter()
93        .map(big_to_fe::<F>)
94        .collect()
95}
96
97pub fn split<F: PrimeField>(value: &F, num_bits: u32) -> (F, F) {
98    let value_big = BigUint::from_bytes_le(value.to_repr().as_ref());
99    let lower_mask = (BigUint::one() << num_bits) - BigUint::one(); // Create a mask for n bits
100    let lower_big = value_big.clone() & &lower_mask;
101    let upper_big = value_big >> num_bits;
102    let lower = big_to_fe::<F>(lower_big);
103    let upper = big_to_fe::<F>(upper_big);
104    (lower, upper)
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use rand_core::OsRng;
111
112    #[test]
113    fn test_decompose() {
114        let ran = JubjubBase::random(&mut OsRng);
115        let limb_bits = 85;
116        let limbs = decompose(&ran, limb_bits);
117        assert_eq!(limbs.len(), 3);
118
119        let mut reconstructed = JubjubBase::ZERO;
120        let base = JubjubBase::from_u128(1_u128 << limb_bits);
121        for (i, limb) in limbs.iter().enumerate() {
122            reconstructed += base.pow_vartime([i as u64]) * limb;
123        }
124        assert_eq!(
125            reconstructed.ct_eq(&ran).unwrap_u8(),
126            1,
127            "Decomposition failed!"
128        );
129    }
130
131    #[test]
132    fn test_split() {
133        let ran = JubjubBase::random(&mut OsRng);
134        let num_bits = 120;
135        let (lower, upper) = split(&ran, num_bits);
136
137        let base = JubjubBase::from_u128(1_u128 << num_bits);
138        let reconstructed = lower + base * upper;
139        assert_eq!(
140            reconstructed.ct_eq(&ran).unwrap_u8(),
141            1,
142            "Splitting failed!"
143        );
144    }
145
146    #[test]
147    fn test_bytes() {
148        let ran = JubjubBase::random(&mut OsRng);
149        let bytes = ran.to_bytes_le();
150
151        let base = jubjub_base_from_le_bytes(&bytes);
152        assert_eq!(ran, base);
153    }
154}