openvm_ecc_circuit/weierstrass_chip/
curves.rs1use 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#[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 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 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 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 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 let lambda = (y2 - y1) * (x2 - x1).invert().unwrap();
186
187 let x3 = lambda.square() - x1 - x2;
189
190 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 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 three_x1_squared * two_y1.invert().unwrap()
206 } else {
207 (three_x1_squared - F::from(NEG_A)) * two_y1.invert().unwrap()
209 };
210
211 let x3 = lambda.square() - x1.double();
213
214 let y3 = lambda * (x1 - x3) - y1;
216
217 (x3, y3)
218}