evm-precompile 1.0.0

Standard EVM precompiles.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
use alloc::{borrow::Cow, vec::Vec};
use bn::{AffineG1, AffineG2, Fq, Fq2, G1, G2, Group, Gt};
use evm::{
	GasMutState,
	interpreter::{ExitError, ExitException, ExitResult, ExitSucceed, utils::u256_to_h256},
};
use primitive_types::U256;

use crate::PurePrecompile;

pub struct Bn128AddByzantium;

impl<G: GasMutState> PurePrecompile<G> for Bn128AddByzantium {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const BYZANTIUM_ADD_GAS_COST: u64 = 500;

		match run_add(input, BYZANTIUM_ADD_GAS_COST, gasometer) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

pub struct Bn128AddIstanbul;

impl<G: GasMutState> PurePrecompile<G> for Bn128AddIstanbul {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const ISTANBUL_ADD_GAS_COST: u64 = 150;

		match run_add(input, ISTANBUL_ADD_GAS_COST, gasometer) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

pub struct Bn128MulByzantium;

impl<G: GasMutState> PurePrecompile<G> for Bn128MulByzantium {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const BYZANTIUM_MUL_GAS_COST: u64 = 40_000;

		match run_mul(input, BYZANTIUM_MUL_GAS_COST, gasometer) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

pub struct Bn128MulIstanbul;

impl<G: GasMutState> PurePrecompile<G> for Bn128MulIstanbul {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const ISTANBUL_MUL_GAS_COST: u64 = 6_000;

		match run_mul(input, ISTANBUL_MUL_GAS_COST, gasometer) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

pub struct Bn128PairingByzantium;

impl<G: GasMutState> PurePrecompile<G> for Bn128PairingByzantium {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000;
		const BYZANTIUM_PAIR_BASE: u64 = 100_000;

		match run_pair(
			input,
			BYZANTIUM_PAIR_PER_POINT,
			BYZANTIUM_PAIR_BASE,
			gasometer,
		) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

pub struct Bn128PairingIstanbul;

impl<G: GasMutState> PurePrecompile<G> for Bn128PairingIstanbul {
	fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
		const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
		const ISTANBUL_PAIR_BASE: u64 = 45_000;

		match run_pair(
			input,
			ISTANBUL_PAIR_PER_POINT,
			ISTANBUL_PAIR_BASE,
			gasometer,
		) {
			Ok((res, out)) => (Ok(res), out),
			Err(err) => (Err(err), Vec::new()),
		}
	}
}

/// FQ_LEN specifies the number of bytes needed to represent an
/// Fq element. This is an element in the base field of BN254.
///
/// Note: The base field is used to define G1 and G2 elements.
const FQ_LEN: usize = 32;

/// SCALAR_LEN specifies the number of bytes needed to represent an Fr element.
/// This is an element in the scalar field of BN254.
const SCALAR_LEN: usize = 32;

/// FQ2_LEN specifies the number of bytes needed to represent an
/// Fq^2 element.
///
/// Note: This is the quadratic extension of Fq, and by definition
/// means we need 2 Fq elements.
const FQ2_LEN: usize = 2 * FQ_LEN;

/// G1_LEN specifies the number of bytes needed to represent a G1 element.
///
/// Note: A G1 element contains 2 Fq elements.
const G1_LEN: usize = 2 * FQ_LEN;
/// G2_LEN specifies the number of bytes needed to represent a G2 element.
///
/// Note: A G2 element contains 2 Fq^2 elements.
const G2_LEN: usize = 2 * FQ2_LEN;

/// Input length for the add operation.
/// `ADD` takes two uncompressed G1 points (64 bytes each).
pub const ADD_INPUT_LEN: usize = 2 * G1_LEN;

/// Input length for the multiplication operation.
/// `MUL` takes an uncompressed G1 point (64 bytes) and scalar (32 bytes).
pub const MUL_INPUT_LEN: usize = G1_LEN + SCALAR_LEN;

/// Pair element length.
/// `PAIR` elements are composed of an uncompressed G1 point (64 bytes) and an uncompressed G2 point
/// (128 bytes).
pub const PAIR_ELEMENT_LEN: usize = G1_LEN + G2_LEN;

/// Run the Bn254 add precompile
pub fn run_add<G: GasMutState>(
	input: &[u8],
	gas_cost: u64,
	gasometer: &mut G,
) -> Result<(ExitSucceed, Vec<u8>), ExitError> {
	gasometer.record_gas(gas_cost.into())?;

	let input = right_pad::<ADD_INPUT_LEN>(input);

	let p1_bytes = &input[..G1_LEN];
	let p2_bytes = &input[G1_LEN..];
	let output = g1_point_add(p1_bytes, p2_bytes)?;

	Ok((ExitSucceed::Returned, output.into()))
}

/// Run the Bn254 mul precompile
pub fn run_mul<G: GasMutState>(
	input: &[u8],
	gas_cost: u64,
	gasometer: &mut G,
) -> Result<(ExitSucceed, Vec<u8>), ExitError> {
	gasometer.record_gas(gas_cost.into())?;

	let input = right_pad::<MUL_INPUT_LEN>(input);

	let point_bytes = &input[..G1_LEN];
	let scalar_bytes = &input[G1_LEN..G1_LEN + SCALAR_LEN];
	let output = g1_point_mul(point_bytes, scalar_bytes)?;

	Ok((ExitSucceed::Returned, output.into()))
}

/// Run the Bn254 pair precompile
pub fn run_pair<G: GasMutState>(
	input: &[u8],
	pair_per_point_cost: u64,
	pair_base_cost: u64,
	gasometer: &mut G,
) -> Result<(ExitSucceed, Vec<u8>), ExitError> {
	let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost;
	gasometer.record_gas(gas_used.into())?;

	if !input.len().is_multiple_of(PAIR_ELEMENT_LEN) {
		return Err(ExitException::OutOfGas.into());
	}

	let elements = input.len() / PAIR_ELEMENT_LEN;

	let mut points = Vec::with_capacity(elements);

	for idx in 0..elements {
		// Offset to the start of the pairing element at index `idx` in the byte slice
		let start = idx * PAIR_ELEMENT_LEN;
		let g1_start = start;
		// Offset to the start of the G2 element in the pairing element
		// This is where G1 ends.
		let g2_start = start + G1_LEN;

		// Get G1 and G2 points from the input
		let encoded_g1_element = &input[g1_start..g2_start];
		let encoded_g2_element = &input[g2_start..g2_start + G2_LEN];
		points.push((encoded_g1_element, encoded_g2_element));
	}

	let pairing_result = pairing_check(&points)?;
	Ok((
		ExitSucceed::Returned,
		u256_to_h256(bool_to_u256(pairing_result)).0.into(),
	))
}

/// Reads a single `Fq` field element from the input slice.
///
/// Takes a byte slice and attempts to interpret the first 32 bytes as an
/// elliptic curve field element. Returns an error if the bytes do not form
/// a valid field element.
///
/// # Panics
///
/// Panics if the input is not at least 32 bytes long.
#[inline]
fn read_fq(input: &[u8]) -> Result<Fq, ExitError> {
	Ok(Fq::from_slice(&input[..FQ_LEN]).map_err(|_| ExitException::OutOfGas)?)
}
/// Reads a Fq2 (quadratic extension field element) from the input slice.
///
/// Parses two consecutive Fq field elements as the real and imaginary parts
/// of an Fq2 element.
/// The second component is parsed before the first, ie if a we represent an
/// element in Fq2 as (x,y) -- `y` is parsed before `x`
///
/// # Panics
///
/// Panics if the input is not at least 64 bytes long.
#[inline]
fn read_fq2(input: &[u8]) -> Result<Fq2, ExitError> {
	let y = read_fq(&input[..FQ_LEN])?;
	let x = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
	Ok(Fq2::new(x, y))
}

/// Creates a new `G1` point from the given `x` and `y` coordinates.
///
/// Constructs a point on the G1 curve from its affine coordinates.
///
/// Note: The point at infinity which is represented as (0,0) is
/// handled specifically because `AffineG1` is not capable of
/// representing such a point.
/// In particular, when we convert from `AffineG1` to `G1`, the point
/// will be (0,0,1) instead of (0,1,0)
#[inline]
fn new_g1_point(px: Fq, py: Fq) -> Result<G1, ExitError> {
	if px == Fq::zero() && py == Fq::zero() {
		Ok(G1::zero())
	} else {
		Ok(AffineG1::new(px, py)
			.map(Into::into)
			.map_err(|_| ExitException::OutOfGas)?)
	}
}

/// Creates a new `G2` point from the given Fq2 coordinates.
///
/// G2 points in BN254 are defined over a quadratic extension field Fq2.
/// This function takes two Fq2 elements representing the x and y coordinates
/// and creates a G2 point.
///
/// Note: The point at infinity which is represented as (0,0) is
/// handled specifically because `AffineG2` is not capable of
/// representing such a point.
/// In particular, when we convert from `AffineG2` to `G2`, the point
/// will be (0,0,1) instead of (0,1,0)
#[inline]
fn new_g2_point(x: Fq2, y: Fq2) -> Result<G2, ExitError> {
	let point = if x.is_zero() && y.is_zero() {
		G2::zero()
	} else {
		G2::from(AffineG2::new(x, y).map_err(|_| ExitException::OutOfGas)?)
	};

	Ok(point)
}

/// Reads a G1 point from the input slice.
///
/// Parses a G1 point from a byte slice by reading two consecutive field elements
/// representing the x and y coordinates.
///
/// # Panics
///
/// Panics if the input is not at least 64 bytes long.
#[inline]
pub fn read_g1_point(input: &[u8]) -> Result<G1, ExitError> {
	let px = read_fq(&input[0..FQ_LEN])?;
	let py = read_fq(&input[FQ_LEN..2 * FQ_LEN])?;
	new_g1_point(px, py)
}

/// Encodes a G1 point into a byte array.
///
/// Converts a G1 point in Jacobian coordinates to affine coordinates and
/// serializes the x and y coordinates as big-endian byte arrays.
///
/// Note: If the point is the point at infinity, this function returns
/// all zeroes.
#[inline]
pub fn encode_g1_point(point: G1) -> [u8; G1_LEN] {
	let mut output = [0u8; G1_LEN];

	if let Some(point_affine) = AffineG1::from_jacobian(point) {
		point_affine
			.x()
			.to_big_endian(&mut output[..FQ_LEN])
			.unwrap();
		point_affine
			.y()
			.to_big_endian(&mut output[FQ_LEN..])
			.unwrap();
	}

	output
}

/// Reads a G2 point from the input slice.
///
/// Parses a G2 point from a byte slice by reading four consecutive Fq field elements
/// representing the two Fq2 coordinates (x and y) of the G2 point.
///
/// # Panics
///
/// Panics if the input is not at least 128 bytes long.
#[inline]
pub fn read_g2_point(input: &[u8]) -> Result<G2, ExitError> {
	let ba = read_fq2(&input[0..FQ2_LEN])?;
	let bb = read_fq2(&input[FQ2_LEN..2 * FQ2_LEN])?;
	new_g2_point(ba, bb)
}

/// Reads a scalar from the input slice
///
/// Note: The scalar does not need to be canonical.
///
/// # Panics
///
/// If `input.len()` is not equal to [`SCALAR_LEN`].
#[inline]
pub fn read_scalar(input: &[u8]) -> bn::Fr {
	assert_eq!(
		input.len(),
		SCALAR_LEN,
		"unexpected scalar length. got {}, expected {SCALAR_LEN}",
		input.len()
	);
	// `Fr::from_slice` can only fail when the length is not `SCALAR_LEN`.
	bn::Fr::from_slice(input).unwrap()
}

/// Performs point addition on two G1 points.
#[inline]
fn g1_point_add(p1_bytes: &[u8], p2_bytes: &[u8]) -> Result<[u8; 64], ExitError> {
	let p1 = read_g1_point(p1_bytes)?;
	let p2 = read_g1_point(p2_bytes)?;
	let result = p1 + p2;
	Ok(encode_g1_point(result))
}

/// Performs a G1 scalar multiplication.
#[inline]
fn g1_point_mul(point_bytes: &[u8], fr_bytes: &[u8]) -> Result<[u8; 64], ExitError> {
	let p = read_g1_point(point_bytes)?;
	let fr = read_scalar(fr_bytes);
	let result = p * fr;
	Ok(encode_g1_point(result))
}

/// pairing_check performs a pairing check on a list of G1 and G2 point pairs and
/// returns true if the result is equal to the identity element.
///
/// Note: If the input is empty, this function returns true.
/// This is different to EIP2537 which disallows the empty input.
#[inline]
fn pairing_check(pairs: &[(&[u8], &[u8])]) -> Result<bool, ExitError> {
	let mut parsed_pairs = Vec::with_capacity(pairs.len());

	for (g1_bytes, g2_bytes) in pairs {
		let g1 = read_g1_point(g1_bytes)?;
		let g2 = read_g2_point(g2_bytes)?;

		// Skip pairs where either point is at infinity
		if !g1.is_zero() && !g2.is_zero() {
			parsed_pairs.push((g1, g2));
		}
	}

	if parsed_pairs.is_empty() {
		return Ok(true);
	}

	Ok(bn::pairing_batch(&parsed_pairs) == Gt::one())
}

/// Right-pads the given slice with zeroes until `LEN`.
///
/// Returns the first `LEN` bytes if it does not need padding.
#[inline]
fn right_pad<const LEN: usize>(data: &[u8]) -> Cow<'_, [u8; LEN]> {
	if let Some(data) = data.get(..LEN) {
		Cow::Borrowed(data.try_into().unwrap())
	} else {
		let mut padded = [0; LEN];
		padded[..data.len()].copy_from_slice(data);
		Cow::Owned(padded)
	}
}

#[inline]
pub const fn bool_to_u256(value: bool) -> U256 {
	if value { U256::one() } else { U256::zero() }
}