pallet_evm_precompile_bls12377/
lib.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20// Arkworks
21use ark_bls12_377::{
22	g1::Config as G1Config, g2::Config as G2Config, Bls12_377, Fq, Fq2, Fr, G1Affine, G1Projective,
23	G2Affine, G2Projective,
24};
25use ark_ec::{
26	hashing::{curve_maps::wb::WBMap, map_to_curve_hasher::MapToCurve, HashToCurveError},
27	pairing::Pairing,
28	AffineRepr, CurveGroup, VariableBaseMSM,
29};
30use ark_ff::{BigInteger384, PrimeField, Zero};
31use ark_std::{ops::Mul, vec::Vec};
32
33// Frontier
34use fp_evm::{
35	ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput,
36	PrecompileResult,
37};
38
39/// Gas discount table for BLS12-377 G1 and G2 multi exponentiation operations.
40const BLS12377_MULTIEXP_DISCOUNT_TABLE: [u16; 128] = [
41	1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322,
42	318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260,
43	259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232,
44	231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213,
45	212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199,
46	198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185,
47	184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174,
48];
49
50/// Encode Fq as `64` bytes by performing Big-Endian encoding of the corresponding (unsigned) integer (top 16 bytes are always zeroes).
51fn encode_fq(field: Fq) -> [u8; 64] {
52	let mut result = [0u8; 64];
53	let rep = field.into_bigint().0;
54
55	result[16..24].copy_from_slice(&rep[5].to_be_bytes());
56	result[24..32].copy_from_slice(&rep[4].to_be_bytes());
57	result[32..40].copy_from_slice(&rep[3].to_be_bytes());
58	result[40..48].copy_from_slice(&rep[2].to_be_bytes());
59	result[48..56].copy_from_slice(&rep[1].to_be_bytes());
60	result[56..64].copy_from_slice(&rep[0].to_be_bytes());
61
62	result
63}
64
65/// Encode point G1 as byte concatenation of encodings of the `x` and `y` affine coordinates.
66fn encode_g1(g1: G1Affine) -> [u8; 128] {
67	let mut result = [0u8; 128];
68	if !g1.is_zero() {
69		result[0..64].copy_from_slice(&encode_fq(g1.x));
70		result[64..128].copy_from_slice(&encode_fq(g1.y));
71	}
72	result
73}
74
75/// Encode point G2 as byte concatenation of encodings of the `x` and `y` affine coordinates.
76fn encode_g2(g2: G2Affine) -> [u8; 256] {
77	let mut result = [0u8; 256];
78	if !g2.is_zero() {
79		result[0..64].copy_from_slice(&encode_fq(g2.x.c0));
80		result[64..128].copy_from_slice(&encode_fq(g2.x.c1));
81		result[128..192].copy_from_slice(&encode_fq(g2.y.c0));
82		result[192..256].copy_from_slice(&encode_fq(g2.y.c1));
83	}
84	result
85}
86
87/// Copy bytes from source.offset to target.
88fn read_input(source: &[u8], target: &mut [u8], offset: usize) {
89	let len = target.len();
90	target[..len].copy_from_slice(&source[offset..][..len]);
91}
92
93/// Decode Fr expects 32 byte input, returns fr in scalar field.
94fn decode_fr(input: &[u8], offset: usize) -> Fr {
95	let mut bytes = [0u8; 32];
96	read_input(input, &mut bytes, offset);
97	Fr::from_be_bytes_mod_order(&bytes)
98}
99
100/// Decode Fq expects 64 byte input with zero top 16 bytes,
101/// returns Fq in base field.
102fn decode_fq(bytes: [u8; 64]) -> Option<Fq> {
103	// check top bytes
104	for b in bytes.iter().take(16) {
105		if b.ne(&0u8) {
106			return None;
107		}
108	}
109
110	let mut tmp = BigInteger384::new([0, 0, 0, 0, 0, 0]);
111	// Note: The following unwraps are if the compiler cannot convert
112	// the byte slice into [u8;8], we know this is infallible since we
113	// are providing the indices at compile time and bytes has a fixed size
114	tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
115	tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
116	tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap());
117	tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap());
118	tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap());
119	tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap());
120
121	Fq::from_bigint(tmp)
122}
123
124fn extract_fq(bytes: [u8; 64]) -> Result<Fq, PrecompileFailure> {
125	let fq = decode_fq(bytes);
126	match fq {
127		None => Err(PrecompileFailure::Error {
128			exit_status: ExitError::Other("invalid Fq".into()),
129		}),
130		Some(c) => Ok(c),
131	}
132}
133
134fn read_fq(input: &[u8], offset: usize) -> Result<Fq, PrecompileFailure> {
135	let mut buf = [0u8; 64];
136	read_input(input, &mut buf, offset);
137	extract_fq(buf)
138}
139
140fn read_fq2(input: &[u8], offset: usize) -> Result<Fq2, PrecompileFailure> {
141	let mut x_buf = [0u8; 64];
142	let mut y_buf = [0u8; 64];
143	read_input(input, &mut x_buf, offset);
144	read_input(input, &mut y_buf, offset + 64);
145	let px = extract_fq(x_buf)?;
146	let py = extract_fq(y_buf)?;
147	Ok(Fq2::new(px, py))
148}
149
150fn map_to_curve_g1(fq: Fq) -> Result<G1Affine, HashToCurveError> {
151	let m2c = WBMap::<G1Config>::new()?;
152	m2c.map_to_curve(fq)
153}
154
155fn map_to_curve_g2(fq2: Fq2) -> Result<G2Affine, HashToCurveError> {
156	let m2c = WBMap::<G2Config>::new()?;
157	m2c.map_to_curve(fq2)
158}
159
160/// Decode G1 given encoded (x, y) coordinates in 128 bytes returns a valid G1 Point.
161fn decode_g1(input: &[u8], offset: usize) -> Result<G1Projective, PrecompileFailure> {
162	let mut px_buf = [0u8; 64];
163	let mut py_buf = [0u8; 64];
164	read_input(input, &mut px_buf, offset);
165	read_input(input, &mut py_buf, offset + 64);
166
167	// Decode x
168	let px = extract_fq(px_buf)?;
169	// Decode y
170	let py = extract_fq(py_buf)?;
171
172	// Check if given input points to infinity
173	if px.is_zero() && py.is_zero() {
174		Ok(G1Projective::zero())
175	} else {
176		let g1 = G1Affine::new_unchecked(px, py);
177		if !g1.is_on_curve() {
178			Err(PrecompileFailure::Error {
179				exit_status: ExitError::Other("point is not on curve".into()),
180			})
181		} else {
182			Ok(g1.into())
183		}
184	}
185}
186
187// Decode G2 given encoded (x, y) coordinates in 256 bytes returns a valid G2 Point.
188fn decode_g2(input: &[u8], offset: usize) -> Result<G2Projective, PrecompileFailure> {
189	let mut px0_buf = [0u8; 64];
190	let mut px1_buf = [0u8; 64];
191	let mut py0_buf = [0u8; 64];
192	let mut py1_buf = [0u8; 64];
193	read_input(input, &mut px0_buf, offset);
194	read_input(input, &mut px1_buf, offset + 64);
195	read_input(input, &mut py0_buf, offset + 128);
196	read_input(input, &mut py1_buf, offset + 192);
197
198	let px0 = extract_fq(px0_buf)?;
199	let px1 = extract_fq(px1_buf)?;
200	let py0 = extract_fq(py0_buf)?;
201	let py1 = extract_fq(py1_buf)?;
202
203	let px = Fq2::new(px0, px1);
204	let py = Fq2::new(py0, py1);
205
206	if px.is_zero() && py.is_zero() {
207		Ok(G2Projective::zero())
208	} else {
209		let g2 = G2Affine::new_unchecked(px, py);
210		if !g2.is_on_curve() {
211			Err(PrecompileFailure::Error {
212				exit_status: ExitError::Other("point is not on curve".into()),
213			})
214		} else {
215			Ok(g2.into())
216		}
217	}
218}
219
220/// Bls12377G1Add implements EIP-2539 G1Add precompile.
221pub struct Bls12377G1Add;
222
223impl Bls12377G1Add {
224	/// https://eips.ethereum.org/EIPS/eip-2539#g1-addition
225	const GAS_COST: u64 = 600;
226}
227
228impl Precompile for Bls12377G1Add {
229	/// Implements EIP-2539 G1Add precompile.
230	/// > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
231	/// > Output is an encoding of addition operation result - single G1 point (`128` bytes).
232	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
233		handle.record_cost(Bls12377G1Add::GAS_COST)?;
234
235		let input = handle.input();
236		if input.len() != 256 {
237			return Err(PrecompileFailure::Error {
238				exit_status: ExitError::Other("invalid input length".into()),
239			});
240		}
241
242		// Decode G1 point p_0
243		let p0 = decode_g1(input, 0)?;
244		// Decode G1 point p_1
245		let p1 = decode_g1(input, 128)?;
246		// Compute r = p_0 + p_1
247		let r = p0 + p1;
248		// Encode the G1 point into 128 bytes output
249		let output = encode_g1(r.into_affine());
250
251		Ok(PrecompileOutput {
252			exit_status: ExitSucceed::Returned,
253			output: output.to_vec(),
254		})
255	}
256}
257
258/// Bls12377G1Mul implements EIP-2539 G1Mul precompile.
259pub struct Bls12377G1Mul;
260
261impl Bls12377G1Mul {
262	/// https://eips.ethereum.org/EIPS/eip-2539#g1-multiplication
263	const GAS_COST: u64 = 12_000;
264}
265
266impl Precompile for Bls12377G1Mul {
267	/// Implements EIP-2539 G1Mul precompile.
268	/// > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
269	/// > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
270	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
271		handle.record_cost(Bls12377G1Mul::GAS_COST)?;
272
273		let input = handle.input();
274		if input.len() != 160 {
275			return Err(PrecompileFailure::Error {
276				exit_status: ExitError::Other("invalid input length".into()),
277			});
278		}
279
280		// Decode G1 point
281		let p = decode_g1(input, 0)?;
282		// Decode scalar value
283		let e = decode_fr(input, 128);
284		// Compute r = e * p
285		let r = p.mul(e);
286		// Encode the G1 point into 128 bytes output
287		let output = encode_g1(r.into_affine());
288
289		Ok(PrecompileOutput {
290			exit_status: ExitSucceed::Returned,
291			output: output.to_vec(),
292		})
293	}
294}
295
296/// Bls12377G1MultiExp implements EIP-2539 G1MultiExp precompile.
297pub struct Bls12377G1MultiExp;
298
299impl Bls12377G1MultiExp {
300	const MULTIPLIER: u64 = 1_000;
301
302	/// Returns the gas required to execute the pre-compiled contract.
303	fn calculate_gas_cost(input_len: usize) -> u64 {
304		// Calculate G1 point, scalar value pair length
305		let k = input_len / 160;
306		if k == 0 {
307			return 0;
308		}
309		// Lookup discount value for G1 point, scalar value pair length
310		let d_len = BLS12377_MULTIEXP_DISCOUNT_TABLE.len();
311		let discount = if k <= d_len {
312			BLS12377_MULTIEXP_DISCOUNT_TABLE[k - 1]
313		} else {
314			BLS12377_MULTIEXP_DISCOUNT_TABLE[d_len - 1]
315		};
316		// Calculate gas and return the result
317		k as u64 * Bls12377G1Mul::GAS_COST * discount as u64 / Bls12377G1MultiExp::MULTIPLIER
318	}
319}
320
321impl Precompile for Bls12377G1MultiExp {
322	/// Implements EIP-2539 G1MultiExp precompile.
323	/// G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
324	/// Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
325	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
326		let gas_cost = Bls12377G1MultiExp::calculate_gas_cost(handle.input().len());
327		handle.record_cost(gas_cost)?;
328
329		let k = handle.input().len() / 160;
330		if handle.input().is_empty() || handle.input().len() % 160 != 0 {
331			return Err(PrecompileFailure::Error {
332				exit_status: ExitError::Other("invalid input length".into()),
333			});
334		}
335
336		let input = handle.input();
337
338		let mut points = Vec::new();
339		let mut scalars = Vec::new();
340		// Decode point scalar pairs
341		for idx in 0..k {
342			let offset = idx * 160;
343			// Decode G1 point
344			let p = decode_g1(input, offset)?;
345			// Decode scalar value
346			let scalar = decode_fr(input, offset + 128);
347			points.push(p.into_affine());
348			scalars.push(scalar);
349		}
350
351		// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
352		let r = G1Projective::msm(&points.to_vec(), &scalars.to_vec()).map_err(|_| {
353			PrecompileFailure::Error {
354				exit_status: ExitError::Other("MSM failed".into()),
355			}
356		})?;
357
358		// Encode the G1 point into 128 bytes output
359		let output = encode_g1(r.into_affine());
360		Ok(PrecompileOutput {
361			exit_status: ExitSucceed::Returned,
362			output: output.to_vec(),
363		})
364	}
365}
366
367/// Bls12377G2Add implements EIP-2539 G2Add precompile.
368pub struct Bls12377G2Add;
369
370impl Bls12377G2Add {
371	/// https://eips.ethereum.org/EIPS/eip-2539#g2-addition
372	const GAS_COST: u64 = 4_500;
373}
374
375impl Precompile for Bls12377G2Add {
376	/// Implements EIP-2539 G2Add precompile.
377	/// > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
378	/// > Output is an encoding of addition operation result - single G2 point (`256` bytes).
379	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
380		handle.record_cost(Bls12377G2Add::GAS_COST)?;
381
382		let input = handle.input();
383		if input.len() != 512 {
384			return Err(PrecompileFailure::Error {
385				exit_status: ExitError::Other("invalid input length".into()),
386			});
387		}
388
389		// Decode G2 point p_0
390		let p0 = decode_g2(input, 0)?;
391		// Decode G2 point p_1
392		let p1 = decode_g2(input, 256)?;
393		// Compute r = p_0 + p_1
394		let r = p0 + p1;
395		// Encode the G2 point into 256 bytes output
396		let output = encode_g2(r.into_affine());
397
398		Ok(PrecompileOutput {
399			exit_status: ExitSucceed::Returned,
400			output: output.to_vec(),
401		})
402	}
403}
404
405/// Bls12377G2Mul implements EIP-2539 G2Mul precompile.
406pub struct Bls12377G2Mul;
407
408impl Bls12377G2Mul {
409	// https://eips.ethereum.org/EIPS/eip-2539#g2-multiplication
410	const GAS_COST: u64 = 55_000;
411}
412
413impl Precompile for Bls12377G2Mul {
414	/// Implements EIP-2539 G2MUL precompile logic.
415	/// > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
416	/// > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
417	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
418		handle.record_cost(Bls12377G2Mul::GAS_COST)?;
419
420		let input = handle.input();
421		if input.len() != 288 {
422			return Err(PrecompileFailure::Error {
423				exit_status: ExitError::Other("invalid input length".into()),
424			});
425		}
426
427		// Decode G2 point
428		let p = decode_g2(input, 0)?;
429		// Decode scalar value
430		let e = decode_fr(input, 256);
431		// Compute r = e * p
432		let r = p.mul(e);
433		// Encode the G2 point into 256 bytes output
434		let output = encode_g2(r.into_affine());
435
436		Ok(PrecompileOutput {
437			exit_status: ExitSucceed::Returned,
438			output: output.to_vec(),
439		})
440	}
441}
442
443// Bls12377G2MultiExp implements EIP-2539 G2MultiExp precompile.
444pub struct Bls12377G2MultiExp;
445
446impl Bls12377G2MultiExp {
447	const MULTIPLIER: u64 = 1_000;
448
449	/// Returns the gas required to execute the pre-compiled contract.
450	fn calculate_gas_cost(input_len: usize) -> u64 {
451		// Calculate G2 point, scalar value pair length
452		let k = input_len / 288;
453		if k == 0 {
454			return 0;
455		}
456		// Lookup discount value for G2 point, scalar value pair length
457		let d_len = BLS12377_MULTIEXP_DISCOUNT_TABLE.len();
458		let discount = if k <= d_len {
459			BLS12377_MULTIEXP_DISCOUNT_TABLE[k - 1]
460		} else {
461			BLS12377_MULTIEXP_DISCOUNT_TABLE[d_len - 1]
462		};
463		// Calculate gas and return the result
464		k as u64 * Bls12377G2Mul::GAS_COST * discount as u64 / Bls12377G2MultiExp::MULTIPLIER
465	}
466}
467
468impl Precompile for Bls12377G2MultiExp {
469	/// Implements EIP-2539 G2MultiExp precompile logic
470	/// > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
471	/// > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
472	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
473		let gas_cost = Bls12377G2MultiExp::calculate_gas_cost(handle.input().len());
474		handle.record_cost(gas_cost)?;
475
476		let k = handle.input().len() / 288;
477		if handle.input().is_empty() || handle.input().len() % 288 != 0 {
478			return Err(PrecompileFailure::Error {
479				exit_status: ExitError::Other("invalid input length".into()),
480			});
481		}
482
483		let input = handle.input();
484
485		let mut points = Vec::new();
486		let mut scalars = Vec::new();
487		// Decode point scalar pairs
488		for idx in 0..k {
489			let offset = idx * 288;
490			// Decode G2 point
491			let p = decode_g2(input, offset)?;
492			// Decode scalar value
493			let scalar = decode_fr(input, offset + 256);
494			points.push(p.into_affine());
495			scalars.push(scalar);
496		}
497
498		// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
499		let r = G2Projective::msm(&points.to_vec(), &scalars.to_vec()).map_err(|_| {
500			PrecompileFailure::Error {
501				exit_status: ExitError::Other("MSM failed".into()),
502			}
503		})?;
504
505		// Encode the G2 point to 256 bytes output
506		let output = encode_g2(r.into_affine());
507		Ok(PrecompileOutput {
508			exit_status: ExitSucceed::Returned,
509			output: output.to_vec(),
510		})
511	}
512}
513
514/// Bls12377Pairing implements EIP-2539 Pairing precompile.
515pub struct Bls12377Pairing;
516
517impl Bls12377Pairing {
518	/// https://eips.ethereum.org/EIPS/eip-2539#pairing-operation
519	const BASE_GAS: u64 = 65_000;
520	const PER_PAIR_GAS: u64 = 55_000;
521}
522
523impl Precompile for Bls12377Pairing {
524	/// Implements EIP-2539 Pairing precompile logic.
525	/// > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
526	/// > - `128` bytes of G1 point encoding
527	/// > - `256` bytes of G2 point encoding
528	/// >   Output is a `32` bytes where last single byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise
529	/// >   (which is equivalent of Big Endian encoding of Solidity values `uint256(1)` and `uin256(0)` respectively).
530	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
531		if handle.input().is_empty() || handle.input().len() % 384 != 0 {
532			return Err(PrecompileFailure::Error {
533				exit_status: ExitError::Other("invalid input length".into()),
534			});
535		}
536
537		let k = handle.input().len() / 384;
538		let gas_cost: u64 = Bls12377Pairing::BASE_GAS + (k as u64 * Bls12377Pairing::PER_PAIR_GAS);
539
540		handle.record_cost(gas_cost)?;
541
542		let input = handle.input();
543
544		let mut a = Vec::new();
545		let mut b = Vec::new();
546		// Decode G1 G2 pairs
547		for idx in 0..k {
548			let offset = idx * 384;
549			// Decode G1 point
550			let g1 = decode_g1(input, offset)?;
551			// Decode G2 point
552			let g2 = decode_g2(input, offset + 128)?;
553
554			// 'point is on curve' check already done,
555			// Here we need to apply subgroup checks.
556			if !g1.into_affine().is_in_correct_subgroup_assuming_on_curve() {
557				return Err(PrecompileFailure::Error {
558					exit_status: ExitError::Other("g1 point is not on correct subgroup".into()),
559				});
560			}
561			if !g2.into_affine().is_in_correct_subgroup_assuming_on_curve() {
562				return Err(PrecompileFailure::Error {
563					exit_status: ExitError::Other("g2 point is not on correct subgroup".into()),
564				});
565			}
566
567			a.push(g1);
568			b.push(g2);
569		}
570
571		let mut output = [0u8; 32];
572		// Compute pairing and set the output
573		if Bls12_377::multi_pairing(a, b).is_zero() {
574			output[31] = 1;
575		}
576
577		Ok(PrecompileOutput {
578			exit_status: ExitSucceed::Returned,
579			output: output.to_vec(),
580		})
581	}
582}
583
584/// Bls12377MapG1 implements EIP-2539 MapG1 precompile.
585pub struct Bls12377MapG1;
586
587impl Bls12377MapG1 {
588	const GAS_COST: u64 = 5_500;
589}
590
591impl Precompile for Bls12377MapG1 {
592	/// Implements EIP-2539 Map_To_G1 precompile.
593	/// > Field-to-curve call expects `64` bytes as an input that is interpreted as an element of the base field.
594	/// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
595	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
596		handle.record_cost(Bls12377MapG1::GAS_COST)?;
597
598		let input = handle.input();
599		if input.len() != 64 {
600			return Err(PrecompileFailure::Error {
601				exit_status: ExitError::Other("invalid input length".into()),
602			});
603		}
604
605		let fq = read_fq(input, 0)?;
606		let g1 = match map_to_curve_g1(fq) {
607			Ok(point) => point.clear_cofactor(),
608			Err(_) => {
609				return Err(PrecompileFailure::Error {
610					exit_status: ExitError::Other("map to curve failed".into()),
611				})
612			}
613		};
614
615		let output = encode_g1(g1);
616		Ok(PrecompileOutput {
617			exit_status: ExitSucceed::Returned,
618			output: output.to_vec(),
619		})
620	}
621}
622
623/// Bls12377MapG2 implements EIP-2539 MapG2 precompile.
624pub struct Bls12377MapG2;
625
626impl Bls12377MapG2 {
627	const GAS_COST: u64 = 75_000;
628}
629
630impl Precompile for Bls12377MapG2 {
631	/// Implements EIP-2539 Map_FP2_TO_G2 precompile logic.
632	/// > Field-to-curve call expects `128` bytes as an input that is interpreted as an element of the quadratic extension field.
633	/// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
634	fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
635		handle.record_cost(Bls12377MapG2::GAS_COST)?;
636
637		let input = handle.input();
638		if input.len() != 128 {
639			return Err(PrecompileFailure::Error {
640				exit_status: ExitError::Other("invalid input length".into()),
641			});
642		}
643
644		let fq2 = read_fq2(input, 0)?;
645		let g2 = match map_to_curve_g2(fq2) {
646			Ok(point) => point.clear_cofactor(),
647			Err(_) => {
648				return Err(PrecompileFailure::Error {
649					exit_status: ExitError::Other("map to curve failed".into()),
650				})
651			}
652		};
653
654		let output = encode_g2(g2);
655		Ok(PrecompileOutput {
656			exit_status: ExitSucceed::Returned,
657			output: output.to_vec(),
658		})
659	}
660}
661
662#[cfg(test)]
663mod tests;