pallet_evm_precompile_bn128/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(unused_crate_dependencies)]
20
21extern crate alloc;
22
23use alloc::vec::Vec;
24use fp_evm::{
25 ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput,
26 PrecompileResult,
27};
28use sp_core::U256;
29
30fn read_input(source: &[u8], target: &mut [u8], offset: usize) {
32 if source.len() <= offset {
34 return;
35 }
36
37 let len = core::cmp::min(target.len(), source.len() - offset);
39 target[..len].copy_from_slice(&source[offset..][..len]);
40}
41
42fn read_fr(input: &[u8], start_inx: usize) -> Result<bn::Fr, PrecompileFailure> {
43 let mut buf = [0u8; 32];
44 read_input(input, &mut buf, start_inx);
45
46 bn::Fr::from_slice(&buf).map_err(|_| PrecompileFailure::Error {
47 exit_status: ExitError::Other("Invalid field element".into()),
48 })
49}
50
51fn read_point(input: &[u8], start_inx: usize) -> Result<bn::G1, PrecompileFailure> {
52 use bn::{AffineG1, Fq, Group, G1};
53
54 let mut px_buf = [0u8; 32];
55 let mut py_buf = [0u8; 32];
56 read_input(input, &mut px_buf, start_inx);
57 read_input(input, &mut py_buf, start_inx + 32);
58
59 let px = Fq::from_slice(&px_buf).map_err(|_| PrecompileFailure::Error {
60 exit_status: ExitError::Other("Invalid point x coordinate".into()),
61 })?;
62
63 let py = Fq::from_slice(&py_buf).map_err(|_| PrecompileFailure::Error {
64 exit_status: ExitError::Other("Invalid point y coordinate".into()),
65 })?;
66
67 Ok(if px == Fq::zero() && py == Fq::zero() {
68 G1::zero()
69 } else {
70 AffineG1::new(px, py)
71 .map_err(|_| PrecompileFailure::Error {
72 exit_status: ExitError::Other("Invalid curve point".into()),
73 })?
74 .into()
75 })
76}
77
78pub struct Bn128Add;
80
81impl Bn128Add {
82 const GAS_COST: u64 = 150; }
84
85impl Precompile for Bn128Add {
86 fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
87 use bn::AffineG1;
88
89 handle.record_cost(Bn128Add::GAS_COST)?;
90
91 let input = handle.input();
92
93 let p1 = read_point(input, 0)?;
94 let p2 = read_point(input, 64)?;
95
96 let mut buf = [0u8; 64];
97 if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
98 sum.x()
100 .to_big_endian(&mut buf[0..32])
101 .map_err(|_| PrecompileFailure::Error {
102 exit_status: ExitError::Other(
103 "Cannot fail since 0..32 is 32-byte length".into(),
104 ),
105 })?;
106 sum.y()
107 .to_big_endian(&mut buf[32..64])
108 .map_err(|_| PrecompileFailure::Error {
109 exit_status: ExitError::Other(
110 "Cannot fail since 32..64 is 32-byte length".into(),
111 ),
112 })?;
113 }
114
115 Ok(PrecompileOutput {
116 exit_status: ExitSucceed::Returned,
117 output: buf.to_vec(),
118 })
119 }
120}
121
122pub struct Bn128Mul;
124
125impl Bn128Mul {
126 const GAS_COST: u64 = 6_000; }
128
129impl Precompile for Bn128Mul {
130 fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
131 use bn::AffineG1;
132
133 handle.record_cost(Bn128Mul::GAS_COST)?;
134
135 let input = handle.input();
136
137 let p = read_point(input, 0)?;
138 let fr = read_fr(input, 64)?;
139
140 let mut buf = [0u8; 64];
141 if let Some(sum) = AffineG1::from_jacobian(p * fr) {
142 sum.x()
144 .to_big_endian(&mut buf[0..32])
145 .map_err(|_| PrecompileFailure::Error {
146 exit_status: ExitError::Other(
147 "Cannot fail since 0..32 is 32-byte length".into(),
148 ),
149 })?;
150 sum.y()
151 .to_big_endian(&mut buf[32..64])
152 .map_err(|_| PrecompileFailure::Error {
153 exit_status: ExitError::Other(
154 "Cannot fail since 32..64 is 32-byte length".into(),
155 ),
156 })?;
157 }
158
159 Ok(PrecompileOutput {
160 exit_status: ExitSucceed::Returned,
161 output: buf.to_vec(),
162 })
163 }
164}
165
166pub struct Bn128Pairing;
168
169impl Bn128Pairing {
170 const BASE_GAS_COST: u64 = 45_000;
172 const GAS_COST_PER_PAIRING: u64 = 34_000;
173}
174
175impl Precompile for Bn128Pairing {
176 fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
177 use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2};
178
179 let ret_val = if handle.input().is_empty() {
180 handle.record_cost(Bn128Pairing::BASE_GAS_COST)?;
181 U256::one()
182 } else {
183 if handle.input().len() % 192 > 0 {
184 return Err(PrecompileFailure::Error {
185 exit_status: ExitError::Other("bad elliptic curve pairing size".into()),
186 });
187 }
188
189 let elements = handle.input().len() / 192;
191
192 let gas_cost: u64 = Bn128Pairing::BASE_GAS_COST
193 + (elements as u64 * Bn128Pairing::GAS_COST_PER_PAIRING);
194
195 handle.record_cost(gas_cost)?;
196
197 let input = handle.input();
198
199 let mut vals = Vec::new();
200 for idx in 0..elements {
201 let a_x = Fq::from_slice(&input[idx * 192..idx * 192 + 32]).map_err(|_| {
202 PrecompileFailure::Error {
203 exit_status: ExitError::Other("Invalid a argument x coordinate".into()),
204 }
205 })?;
206
207 let a_y = Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64]).map_err(|_| {
208 PrecompileFailure::Error {
209 exit_status: ExitError::Other("Invalid a argument y coordinate".into()),
210 }
211 })?;
212
213 let b_a_y =
214 Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96]).map_err(|_| {
215 PrecompileFailure::Error {
216 exit_status: ExitError::Other(
217 "Invalid b argument imaginary coeff x coordinate".into(),
218 ),
219 }
220 })?;
221
222 let b_a_x =
223 Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128]).map_err(|_| {
224 PrecompileFailure::Error {
225 exit_status: ExitError::Other(
226 "Invalid b argument imaginary coeff y coordinate".into(),
227 ),
228 }
229 })?;
230
231 let b_b_y =
232 Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160]).map_err(|_| {
233 PrecompileFailure::Error {
234 exit_status: ExitError::Other(
235 "Invalid b argument real coeff x coordinate".into(),
236 ),
237 }
238 })?;
239
240 let b_b_x =
241 Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192]).map_err(|_| {
242 PrecompileFailure::Error {
243 exit_status: ExitError::Other(
244 "Invalid b argument real coeff y coordinate".into(),
245 ),
246 }
247 })?;
248
249 let b_a = Fq2::new(b_a_x, b_a_y);
250 let b_b = Fq2::new(b_b_x, b_b_y);
251 let b = if b_a.is_zero() && b_b.is_zero() {
252 G2::zero()
253 } else {
254 G2::from(
255 AffineG2::new(b_a, b_b).map_err(|_| PrecompileFailure::Error {
256 exit_status: ExitError::Other(
257 "Invalid b argument - not on curve".into(),
258 ),
259 })?,
260 )
261 };
262 let a = if a_x.is_zero() && a_y.is_zero() {
263 G1::zero()
264 } else {
265 G1::from(
266 AffineG1::new(a_x, a_y).map_err(|_| PrecompileFailure::Error {
267 exit_status: ExitError::Other(
268 "Invalid a argument - not on curve".into(),
269 ),
270 })?,
271 )
272 };
273 vals.push((a, b));
274 }
275
276 let mul = pairing_batch(&vals);
277
278 if mul == Gt::one() {
279 U256::one()
280 } else {
281 U256::zero()
282 }
283 };
284
285 Ok(PrecompileOutput {
286 exit_status: ExitSucceed::Returned,
287 output: ret_val.to_big_endian().to_vec(),
288 })
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use pallet_evm_test_vector_support::test_precompile_test_vectors;
296
297 #[test]
298 fn process_consensus_tests_for_add() -> Result<(), String> {
299 test_precompile_test_vectors::<Bn128Add>("../testdata/common_bnadd.json")?;
300 Ok(())
301 }
302
303 #[test]
304 fn process_consensus_tests_for_mul() -> Result<(), String> {
305 test_precompile_test_vectors::<Bn128Mul>("../testdata/common_bnmul.json")?;
306 Ok(())
307 }
308
309 #[test]
310 fn process_consensus_tests_for_pair() -> Result<(), String> {
311 test_precompile_test_vectors::<Bn128Pairing>("../testdata/common_bnpair.json")?;
312 Ok(())
313 }
314}