openvm_ecc_circuit/weierstrass_chip/
curves.rs

1use halo2curves_axiom::ff::{Field, PrimeField};
2use num_bigint::BigUint;
3use num_traits::Num;
4use once_cell::sync::Lazy;
5use openvm_algebra_circuit::fields::{
6    blocks_to_field_element, blocks_to_field_element_bls12_381_coordinate, field_element_to_blocks,
7    field_element_to_blocks_bls12_381_coordinate, FieldType,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum CurveType {
12    K256 = 0,
13    P256 = 1,
14    BN254 = 2,
15    BLS12_381 = 3,
16}
17
18const P256_NEG_A: u64 = 3;
19
20fn get_modulus_as_bigint<F: PrimeField>() -> BigUint {
21    BigUint::from_str_radix(F::MODULUS.trim_start_matches("0x"), 16).unwrap()
22}
23
24static K256_COORD_MODULUS: Lazy<BigUint> =
25    Lazy::new(get_modulus_as_bigint::<halo2curves_axiom::secq256k1::Fq>);
26static P256_COORD_MODULUS: Lazy<BigUint> =
27    Lazy::new(get_modulus_as_bigint::<halo2curves_axiom::secp256r1::Fp>);
28static BN254_COORD_MODULUS: Lazy<BigUint> =
29    Lazy::new(get_modulus_as_bigint::<halo2curves_axiom::bn256::Fq>);
30static BLS12_381_COORD_MODULUS: Lazy<BigUint> =
31    Lazy::new(get_modulus_as_bigint::<halo2curves_axiom::bls12_381::Fq>);
32static P256_A_COEFF: Lazy<BigUint> = Lazy::new(|| {
33    BigUint::from_bytes_le(&(-halo2curves_axiom::secp256r1::Fp::from(P256_NEG_A)).to_bytes())
34});
35
36pub(super) fn get_curve_type(modulus: &BigUint, a_coeff: &BigUint) -> Option<CurveType> {
37    if modulus == &*K256_COORD_MODULUS && a_coeff == &BigUint::ZERO {
38        return Some(CurveType::K256);
39    }
40
41    if modulus == &*P256_COORD_MODULUS && a_coeff == &*P256_A_COEFF {
42        return Some(CurveType::P256);
43    }
44
45    if modulus == &*BN254_COORD_MODULUS && a_coeff == &BigUint::ZERO {
46        return Some(CurveType::BN254);
47    }
48
49    if modulus == &*BLS12_381_COORD_MODULUS && a_coeff == &BigUint::ZERO {
50        return Some(CurveType::BLS12_381);
51    }
52
53    None
54}
55
56#[inline(always)]
57pub fn ec_add_ne<const FIELD_TYPE: u8, const BLOCKS: usize, const BLOCK_SIZE: usize>(
58    input_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
59) -> [[u8; BLOCK_SIZE]; BLOCKS] {
60    match FIELD_TYPE {
61        x if x == FieldType::K256Coordinate as u8 => {
62            ec_add_ne_256bit::<halo2curves_axiom::secq256k1::Fq, BLOCKS, BLOCK_SIZE>(input_data)
63        }
64        x if x == FieldType::P256Coordinate as u8 => {
65            ec_add_ne_256bit::<halo2curves_axiom::secp256r1::Fp, BLOCKS, BLOCK_SIZE>(input_data)
66        }
67        x if x == FieldType::BN254Coordinate as u8 => {
68            ec_add_ne_256bit::<halo2curves_axiom::bn256::Fq, BLOCKS, BLOCK_SIZE>(input_data)
69        }
70        x if x == FieldType::BLS12_381Coordinate as u8 => {
71            ec_add_ne_bls12_381::<BLOCKS, BLOCK_SIZE>(input_data)
72        }
73        _ => panic!("Unsupported field type: {FIELD_TYPE}"),
74    }
75}
76
77/// Dispatch elliptic curve point doubling based on const generic curve type
78#[inline(always)]
79pub fn ec_double<const CURVE_TYPE: u8, const BLOCKS: usize, const BLOCK_SIZE: usize>(
80    input_data: [[u8; BLOCK_SIZE]; BLOCKS],
81) -> [[u8; BLOCK_SIZE]; BLOCKS] {
82    match CURVE_TYPE {
83        x if x == CurveType::K256 as u8 => {
84            ec_double_256bit::<halo2curves_axiom::secq256k1::Fq, 0, BLOCKS, BLOCK_SIZE>(input_data)
85        }
86        x if x == CurveType::P256 as u8 => {
87            ec_double_256bit::<halo2curves_axiom::secp256r1::Fp, P256_NEG_A, BLOCKS, BLOCK_SIZE>(
88                input_data,
89            )
90        }
91        x if x == CurveType::BN254 as u8 => {
92            ec_double_256bit::<halo2curves_axiom::bn256::Fq, 0, BLOCKS, BLOCK_SIZE>(input_data)
93        }
94        x if x == CurveType::BLS12_381 as u8 => {
95            ec_double_bls12_381::<BLOCKS, BLOCK_SIZE>(input_data)
96        }
97        _ => panic!("Unsupported curve type: {CURVE_TYPE}"),
98    }
99}
100
101#[inline(always)]
102fn ec_add_ne_256bit<
103    F: PrimeField<Repr = [u8; 32]>,
104    const BLOCKS: usize,
105    const BLOCK_SIZE: usize,
106>(
107    input_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
108) -> [[u8; BLOCK_SIZE]; BLOCKS] {
109    let x1 = blocks_to_field_element::<F>(input_data[0][..BLOCKS / 2].as_flattened());
110    let y1 = blocks_to_field_element::<F>(input_data[0][BLOCKS / 2..].as_flattened());
111    let x2 = blocks_to_field_element::<F>(input_data[1][..BLOCKS / 2].as_flattened());
112    let y2 = blocks_to_field_element::<F>(input_data[1][BLOCKS / 2..].as_flattened());
113
114    let (x3, y3) = ec_add_ne_impl::<F>(x1, y1, x2, y2);
115
116    let mut output = [[0u8; BLOCK_SIZE]; BLOCKS];
117    field_element_to_blocks::<F, BLOCK_SIZE>(&x3, &mut output[..BLOCKS / 2]);
118    field_element_to_blocks::<F, BLOCK_SIZE>(&y3, &mut output[BLOCKS / 2..]);
119    output
120}
121
122#[inline(always)]
123fn ec_double_256bit<
124    F: PrimeField<Repr = [u8; 32]>,
125    const NEG_A: u64,
126    const BLOCKS: usize,
127    const BLOCK_SIZE: usize,
128>(
129    input_data: [[u8; BLOCK_SIZE]; BLOCKS],
130) -> [[u8; BLOCK_SIZE]; BLOCKS] {
131    let x1 = blocks_to_field_element::<F>(input_data[..BLOCKS / 2].as_flattened());
132    let y1 = blocks_to_field_element::<F>(input_data[BLOCKS / 2..].as_flattened());
133
134    let (x3, y3) = ec_double_impl::<F, NEG_A>(x1, y1);
135
136    let mut output = [[0u8; BLOCK_SIZE]; BLOCKS];
137    field_element_to_blocks::<F, BLOCK_SIZE>(&x3, &mut output[..BLOCKS / 2]);
138    field_element_to_blocks::<F, BLOCK_SIZE>(&y3, &mut output[BLOCKS / 2..]);
139    output
140}
141
142#[inline(always)]
143fn ec_add_ne_bls12_381<const BLOCKS: usize, const BLOCK_SIZE: usize>(
144    input_data: [[[u8; BLOCK_SIZE]; BLOCKS]; 2],
145) -> [[u8; BLOCK_SIZE]; BLOCKS] {
146    // Extract coordinates
147    let x1 =
148        blocks_to_field_element_bls12_381_coordinate(input_data[0][..BLOCKS / 2].as_flattened());
149    let y1 =
150        blocks_to_field_element_bls12_381_coordinate(input_data[0][BLOCKS / 2..].as_flattened());
151    let x2 =
152        blocks_to_field_element_bls12_381_coordinate(input_data[1][..BLOCKS / 2].as_flattened());
153    let y2 =
154        blocks_to_field_element_bls12_381_coordinate(input_data[1][BLOCKS / 2..].as_flattened());
155
156    let (x3, y3) = ec_add_ne_impl::<blstrs::Fp>(x1, y1, x2, y2);
157
158    // Final output
159    let mut output = [[0u8; BLOCK_SIZE]; BLOCKS];
160    field_element_to_blocks_bls12_381_coordinate(&x3, &mut output[..BLOCKS / 2]);
161    field_element_to_blocks_bls12_381_coordinate(&y3, &mut output[BLOCKS / 2..]);
162    output
163}
164
165#[inline(always)]
166fn ec_double_bls12_381<const BLOCKS: usize, const BLOCK_SIZE: usize>(
167    input_data: [[u8; BLOCK_SIZE]; BLOCKS],
168) -> [[u8; BLOCK_SIZE]; BLOCKS] {
169    // Extract coordinates
170    let x1 = blocks_to_field_element_bls12_381_coordinate(input_data[..BLOCKS / 2].as_flattened());
171    let y1 = blocks_to_field_element_bls12_381_coordinate(input_data[BLOCKS / 2..].as_flattened());
172
173    let (x3, y3) = ec_double_impl::<blstrs::Fp, 0>(x1, y1);
174
175    // Final output
176    let mut output = [[0u8; BLOCK_SIZE]; BLOCKS];
177    field_element_to_blocks_bls12_381_coordinate(&x3, &mut output[..BLOCKS / 2]);
178    field_element_to_blocks_bls12_381_coordinate(&y3, &mut output[BLOCKS / 2..]);
179    output
180}
181
182#[inline(always)]
183pub fn ec_add_ne_impl<F: Field>(x1: F, y1: F, x2: F, y2: F) -> (F, F) {
184    // Calculate lambda = (y2 - y1) / (x2 - x1)
185    let lambda = (y2 - y1) * (x2 - x1).invert().unwrap();
186
187    // Calculate x3 = lambda^2 - x1 - x2
188    let x3 = lambda.square() - x1 - x2;
189
190    // Calculate y3 = lambda * (x1 - x3) - y1
191    let y3 = lambda * (x1 - x3) - y1;
192
193    (x3, y3)
194}
195
196#[inline(always)]
197pub fn ec_double_impl<F: Field + From<u64>, const NEG_A: u64>(x1: F, y1: F) -> (F, F) {
198    // Calculate lambda based on curve coefficient 'a'
199    let x1_squared = x1.square();
200    let three_x1_squared = x1_squared + x1_squared.double();
201    let two_y1 = y1.double();
202
203    let lambda = if NEG_A == 0 {
204        // For a = 0: lambda = (3 * x1^2) / (2 * y1)
205        three_x1_squared * two_y1.invert().unwrap()
206    } else {
207        // lambda = (3 * x1^2 + a) / (2 * y1)
208        (three_x1_squared - F::from(NEG_A)) * two_y1.invert().unwrap()
209    };
210
211    // Calculate x3 = lambda^2 - 2 * x1
212    let x3 = lambda.square() - x1.double();
213
214    // Calculate y3 = lambda * (x1 - x3) - y1
215    let y3 = lambda * (x1 - x3) - y1;
216
217    (x3, y3)
218}