1use 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
31fn 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 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 let bytes: [u8; 32] = [0xFF; 32];
63 let max_256_bit_uint = BigUint::from_bytes_be(&bytes);
64
65 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
91impl 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 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 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 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 let (r, gas_cost) = if base_len == 0 && mod_len == 0 {
163 (BigUint::zero(), MIN_GAS_COST)
164 } else {
165 let base_start = 96; 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 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 let bytes = r.to_bytes_be();
195
196 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 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); 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"); }
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 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); 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"); }
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); 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"); }
430 }
431 }
432
433 #[test]
434 fn test_zero_exp_with_33_length() {
435 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); 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}