ovr_evm_precompile_modexp/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// This file is part of Frontier.
3//
4// Copyright (c) 2020 Parity Technologies (UK) Ltd.
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
18use core::{cmp::max, ops::BitAnd};
19
20use num::{BigUint, FromPrimitive, One, ToPrimitive, Zero};
21
22use fp_evm::{
23	Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput,
24	PrecompileResult,
25};
26
27pub struct Modexp;
28
29const MIN_GAS_COST: u64 = 200;
30
31// Calculate gas cost according to EIP 2565:
32// https://eips.ethereum.org/EIPS/eip-2565
33fn calculate_gas_cost(
34	base_length: u64,
35	exp_length: u64,
36	mod_length: u64,
37	exponent: &BigUint,
38) -> u64 {
39	fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 {
40		let max_length = max(base_length, mod_length);
41		let mut words = max_length / 8;
42		if max_length % 8 > 0 {
43			words += 1;
44		}
45
46		// Note: can't overflow because we take words to be some u64 value / 8, which is
47		// necessarily less than sqrt(u64::MAX).
48		// Additionally, both base_length and mod_length are bounded to 1024, so this has
49		// an upper bound of roughly (1024 / 8) squared
50		words * words
51	}
52
53	fn calculate_iteration_count(exp_length: u64, exponent: &BigUint) -> u64 {
54		let mut iteration_count: u64 = 0;
55
56		if exp_length <= 32 && exponent.is_zero() {
57			iteration_count = 0;
58		} else if exp_length <= 32 {
59			iteration_count = exponent.bits() - 1;
60		} else if exp_length > 32 {
61			// construct BigUint to represent (2^256) - 1
62			let bytes: [u8; 32] = [0xFF; 32];
63			let max_256_bit_uint = BigUint::from_bytes_be(&bytes);
64
65			// from the EIP spec:
66			// (8 * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1)
67			//
68			// Notes:
69			// * exp_length is bounded to 1024 and is > 32
70			// * exponent can be zero, so we subtract 1 after adding the other terms (whose sum
71			//   must be > 0)
72			// * the addition can't overflow because the terms are both capped at roughly
73			//   8 * max size of exp_length (1024)
74			iteration_count =
75				(8 * (exp_length - 32)) + exponent.bitand(max_256_bit_uint).bits() - 1;
76		}
77
78		max(iteration_count, 1)
79	}
80
81	let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length);
82	let iteration_count = calculate_iteration_count(exp_length, exponent);
83	let gas = max(
84		MIN_GAS_COST,
85		multiplication_complexity * iteration_count / 3,
86	);
87
88	gas
89}
90
91// ModExp expects the following as inputs:
92// 1) 32 bytes expressing the length of base
93// 2) 32 bytes expressing the length of exponent
94// 3) 32 bytes expressing the length of modulus
95// 4) base, size as described above
96// 5) exponent, size as described above
97// 6) modulus, size as described above
98//
99//
100// NOTE: input sizes are bound to 1024 bytes, with the expectation
101//       that gas limits would be applied before actual computation.
102//
103//       maximum stack size will also prevent abuse.
104//
105//       see: https://eips.ethereum.org/EIPS/eip-198
106
107impl Precompile for Modexp {
108	fn execute(
109		input: &[u8],
110		target_gas: Option<u64>,
111		_context: &Context,
112		_is_static: bool,
113	) -> PrecompileResult {
114		if input.len() < 96 {
115			return Err(PrecompileFailure::Error {
116				exit_status: ExitError::Other("input must contain at least 96 bytes".into()),
117			});
118		};
119
120		// reasonable assumption: this must fit within the Ethereum EVM's max stack size
121		let max_size_big = BigUint::from_u32(1024).expect("can't create BigUint");
122
123		let mut buf = [0; 32];
124		buf.copy_from_slice(&input[0..32]);
125		let base_len_big = BigUint::from_bytes_be(&buf);
126		if base_len_big > max_size_big {
127			return Err(PrecompileFailure::Error {
128				exit_status: ExitError::Other("unreasonably large base length".into()),
129			});
130		}
131
132		buf.copy_from_slice(&input[32..64]);
133		let exp_len_big = BigUint::from_bytes_be(&buf);
134		if exp_len_big > max_size_big {
135			return Err(PrecompileFailure::Error {
136				exit_status: ExitError::Other("unreasonably large exponent length".into()),
137			});
138		}
139
140		buf.copy_from_slice(&input[64..96]);
141		let mod_len_big = BigUint::from_bytes_be(&buf);
142		if mod_len_big > max_size_big {
143			return Err(PrecompileFailure::Error {
144				exit_status: ExitError::Other("unreasonably large modulus length".into()),
145			});
146		}
147
148		// bounds check handled above
149		let base_len = base_len_big.to_usize().expect("base_len out of bounds");
150		let exp_len = exp_len_big.to_usize().expect("exp_len out of bounds");
151		let mod_len = mod_len_big.to_usize().expect("mod_len out of bounds");
152
153		// input length should be at least 96 + user-specified length of base + exp + mod
154		let total_len = base_len + exp_len + mod_len + 96;
155		if input.len() < total_len {
156			return Err(PrecompileFailure::Error {
157				exit_status: ExitError::Other("insufficient input size".into()),
158			});
159		}
160
161		// Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first.
162		let (r, gas_cost) = if base_len == 0 && mod_len == 0 {
163			(BigUint::zero(), MIN_GAS_COST)
164		} else {
165			// read the numbers themselves.
166			let base_start = 96; // previous 3 32-byte fields
167			let base = BigUint::from_bytes_be(&input[base_start..base_start + base_len]);
168
169			let exp_start = base_start + base_len;
170			let exponent = BigUint::from_bytes_be(&input[exp_start..exp_start + exp_len]);
171
172			// do our gas accounting
173			let gas_cost =
174				calculate_gas_cost(base_len as u64, exp_len as u64, mod_len as u64, &exponent);
175			if let Some(gas_left) = target_gas {
176				if gas_left < gas_cost {
177					return Err(PrecompileFailure::Error {
178						exit_status: ExitError::OutOfGas,
179					});
180				}
181			};
182
183			let mod_start = exp_start + exp_len;
184			let modulus = BigUint::from_bytes_be(&input[mod_start..mod_start + mod_len]);
185
186			if modulus.is_zero() || modulus.is_one() {
187				(BigUint::zero(), gas_cost)
188			} else {
189				(base.modpow(&exponent, &modulus), gas_cost)
190			}
191		};
192
193		// write output to given memory, left padded and same length as the modulus.
194		let bytes = r.to_bytes_be();
195
196		// always true except in the case of zero-length modulus, which leads to
197		// output of length and value 1.
198		if bytes.len() == mod_len {
199			Ok(PrecompileOutput {
200				exit_status: ExitSucceed::Returned,
201				cost: gas_cost,
202				output: bytes.to_vec(),
203				logs: Default::default(),
204			})
205		} else if bytes.len() < mod_len {
206			let mut ret = Vec::with_capacity(mod_len);
207			ret.extend(core::iter::repeat(0).take(mod_len - bytes.len()));
208			ret.extend_from_slice(&bytes[..]);
209			Ok(PrecompileOutput {
210				exit_status: ExitSucceed::Returned,
211				cost: gas_cost,
212				output: ret.to_vec(),
213				logs: Default::default(),
214			})
215		} else {
216			Err(PrecompileFailure::Error {
217				exit_status: ExitError::Other("failed".into()),
218			})
219		}
220	}
221}
222
223#[cfg(test)]
224mod tests {
225	use super::*;
226	use ovr_evm_test_vector_support::test_precompile_test_vectors;
227
228	#[test]
229	fn process_consensus_tests() -> Result<(), String> {
230		test_precompile_test_vectors::<Modexp>("../testdata/modexp_eip2565.json")?;
231		Ok(())
232	}
233
234	#[test]
235	fn test_empty_input() -> Result<(), PrecompileFailure> {
236		let input: [u8; 0] = [];
237
238		let cost: u64 = 1;
239
240		let context: Context = Context {
241			address: Default::default(),
242			caller: Default::default(),
243			apparent_value: From::from(0),
244		};
245
246		match Modexp::execute(&input, Some(cost), &context, false) {
247			Ok(_) => {
248				panic!("Test not expected to pass");
249			}
250			Err(e) => {
251				assert_eq!(
252					e,
253					PrecompileFailure::Error {
254						exit_status: ExitError::Other(
255							"input must contain at least 96 bytes".into()
256						)
257					}
258				);
259				Ok(())
260			}
261		}
262	}
263
264	#[test]
265	fn test_insufficient_input() -> Result<(), PrecompileFailure> {
266		let input = hex::decode(
267			"0000000000000000000000000000000000000000000000000000000000000001\
268			0000000000000000000000000000000000000000000000000000000000000001\
269			0000000000000000000000000000000000000000000000000000000000000001",
270		)
271		.expect("Decode failed");
272
273		let cost: u64 = 1;
274
275		let context: Context = Context {
276			address: Default::default(),
277			caller: Default::default(),
278			apparent_value: From::from(0),
279		};
280
281		match Modexp::execute(&input, Some(cost), &context, false) {
282			Ok(_) => {
283				panic!("Test not expected to pass");
284			}
285			Err(e) => {
286				assert_eq!(
287					e,
288					PrecompileFailure::Error {
289						exit_status: ExitError::Other("insufficient input size".into())
290					}
291				);
292				Ok(())
293			}
294		}
295	}
296
297	#[test]
298	fn test_excessive_input() -> Result<(), PrecompileFailure> {
299		let input = hex::decode(
300			"1000000000000000000000000000000000000000000000000000000000000001\
301			0000000000000000000000000000000000000000000000000000000000000001\
302			0000000000000000000000000000000000000000000000000000000000000001",
303		)
304		.expect("Decode failed");
305
306		let cost: u64 = 1;
307
308		let context: Context = Context {
309			address: Default::default(),
310			caller: Default::default(),
311			apparent_value: From::from(0),
312		};
313
314		match Modexp::execute(&input, Some(cost), &context, false) {
315			Ok(_) => {
316				panic!("Test not expected to pass");
317			}
318			Err(e) => {
319				assert_eq!(
320					e,
321					PrecompileFailure::Error {
322						exit_status: ExitError::Other("unreasonably large base length".into())
323					}
324				);
325				Ok(())
326			}
327		}
328	}
329
330	#[test]
331	fn test_simple_inputs() {
332		let input = hex::decode(
333			"0000000000000000000000000000000000000000000000000000000000000001\
334			0000000000000000000000000000000000000000000000000000000000000001\
335			0000000000000000000000000000000000000000000000000000000000000001\
336			03\
337			05\
338			07",
339		)
340		.expect("Decode failed");
341
342		// 3 ^ 5 % 7 == 5
343
344		let cost: u64 = 100000;
345
346		let context: Context = Context {
347			address: Default::default(),
348			caller: Default::default(),
349			apparent_value: From::from(0),
350		};
351
352		match Modexp::execute(&input, Some(cost), &context, false) {
353			Ok(precompile_result) => {
354				assert_eq!(precompile_result.output.len(), 1); // should be same length as mod
355				let result = BigUint::from_bytes_be(&precompile_result.output[..]);
356				let expected = BigUint::parse_bytes(b"5", 10).unwrap();
357				assert_eq!(result, expected);
358			}
359			Err(_) => {
360				panic!("Modexp::execute() returned error"); // TODO: how to pass error on?
361			}
362		}
363	}
364
365	#[test]
366	fn test_large_inputs() {
367		let input = hex::decode(
368			"0000000000000000000000000000000000000000000000000000000000000020\
369			0000000000000000000000000000000000000000000000000000000000000020\
370			0000000000000000000000000000000000000000000000000000000000000020\
371			000000000000000000000000000000000000000000000000000000000000EA5F\
372			0000000000000000000000000000000000000000000000000000000000000015\
373			0000000000000000000000000000000000000000000000000000000000003874",
374		)
375		.expect("Decode failed");
376
377		// 59999 ^ 21 % 14452 = 10055
378
379		let cost: u64 = 100000;
380
381		let context: Context = Context {
382			address: Default::default(),
383			caller: Default::default(),
384			apparent_value: From::from(0),
385		};
386
387		match Modexp::execute(&input, Some(cost), &context, false) {
388			Ok(precompile_result) => {
389				assert_eq!(precompile_result.output.len(), 32); // should be same length as mod
390				let result = BigUint::from_bytes_be(&precompile_result.output[..]);
391				let expected = BigUint::parse_bytes(b"10055", 10).unwrap();
392				assert_eq!(result, expected);
393			}
394			Err(_) => {
395				panic!("Modexp::execute() returned error"); // TODO: how to pass error on?
396			}
397		}
398	}
399
400	#[test]
401	fn test_large_computation() {
402		let input = hex::decode(
403			"0000000000000000000000000000000000000000000000000000000000000001\
404			0000000000000000000000000000000000000000000000000000000000000020\
405			0000000000000000000000000000000000000000000000000000000000000020\
406			03\
407			fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\
408			fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
409		)
410		.expect("Decode failed");
411
412		let cost: u64 = 100000;
413
414		let context: Context = Context {
415			address: Default::default(),
416			caller: Default::default(),
417			apparent_value: From::from(0),
418		};
419
420		match Modexp::execute(&input, Some(cost), &context, false) {
421			Ok(precompile_result) => {
422				assert_eq!(precompile_result.output.len(), 32); // should be same length as mod
423				let result = BigUint::from_bytes_be(&precompile_result.output[..]);
424				let expected = BigUint::parse_bytes(b"1", 10).unwrap();
425				assert_eq!(result, expected);
426			}
427			Err(_) => {
428				panic!("Modexp::execute() returned error"); // TODO: how to pass error on?
429			}
430		}
431	}
432
433	#[test]
434	fn test_zero_exp_with_33_length() {
435		// This is a regression test which ensures that the 'iteration_count' calculation
436		// in 'calculate_iteration_count' cannot underflow.
437		//
438		// In debug mode, this underflow could cause a panic. Otherwise, it causes N**0 to
439		// be calculated at more-than-normal expense.
440		//
441		// TODO: cite security advisory
442
443		let input = vec![
444			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
445			0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446			0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
447			0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
448			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
449		];
450
451		let cost: u64 = 100000;
452
453		let context: Context = Context {
454			address: Default::default(),
455			caller: Default::default(),
456			apparent_value: From::from(0),
457		};
458
459		let precompile_result = Modexp::execute(&input, Some(cost), &context, false)
460			.expect("Modexp::execute() returned error");
461
462		assert_eq!(precompile_result.output.len(), 1); // should be same length as mod
463		let result = BigUint::from_bytes_be(&precompile_result.output[..]);
464		let expected = BigUint::parse_bytes(b"0", 10).unwrap();
465		assert_eq!(result, expected);
466	}
467}