ethcore: minor optimization of modexp by using LR exponentiation (#9697)
This commit is contained in:
		
							parent
							
								
									5a2f3e700b
								
							
						
					
					
						commit
						726884afcb
					
				@ -25,12 +25,10 @@ extern crate ethereum_types;
 | 
			
		||||
extern crate parity_bytes as bytes;
 | 
			
		||||
extern crate rustc_hex;
 | 
			
		||||
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
 | 
			
		||||
use bytes::BytesRef;
 | 
			
		||||
use ethcore::builtin::Builtin;
 | 
			
		||||
use ethcore::machine::EthereumMachine;
 | 
			
		||||
use ethereum_types::{Address, U256};
 | 
			
		||||
use ethereum_types::U256;
 | 
			
		||||
use ethcore::ethereum::new_byzantium_test_machine;
 | 
			
		||||
use rustc_hex::FromHex;
 | 
			
		||||
use self::test::Bencher;
 | 
			
		||||
 | 
			
		||||
@ -311,35 +311,51 @@ impl Impl for Ripemd160 {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// calculate modexp: exponentiation by squaring. the `num` crate has pow, but not modular.
 | 
			
		||||
fn modexp(mut base: BigUint, mut exp: BigUint, modulus: BigUint) -> BigUint {
 | 
			
		||||
	use num::Integer;
 | 
			
		||||
// calculate modexp: left-to-right binary exponentiation to keep multiplicands lower
 | 
			
		||||
fn modexp(mut base: BigUint, exp: Vec<u8>, modulus: BigUint) -> BigUint {
 | 
			
		||||
	const BITS_PER_DIGIT: usize = 8;
 | 
			
		||||
 | 
			
		||||
	if modulus <= BigUint::one() { // n^m % 0 || n^m % 1
 | 
			
		||||
	// n^m % 0 || n^m % 1
 | 
			
		||||
	if modulus <= BigUint::one() {
 | 
			
		||||
		return BigUint::zero();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if exp.is_zero() { // n^0 % m
 | 
			
		||||
	// normalize exponent
 | 
			
		||||
	let mut exp = exp.into_iter().skip_while(|d| *d == 0).peekable();
 | 
			
		||||
 | 
			
		||||
	// n^0 % m
 | 
			
		||||
	if let None = exp.peek() {
 | 
			
		||||
		return BigUint::one();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if base.is_zero() { // 0^n % m, n>0
 | 
			
		||||
	// 0^n % m, n > 0
 | 
			
		||||
	if base.is_zero() {
 | 
			
		||||
		return BigUint::zero();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let mut result = BigUint::one();
 | 
			
		||||
	base = base % &modulus;
 | 
			
		||||
 | 
			
		||||
	// fast path for base divisible by modulus.
 | 
			
		||||
	// Fast path for base divisible by modulus.
 | 
			
		||||
	if base.is_zero() { return BigUint::zero() }
 | 
			
		||||
	while !exp.is_zero() {
 | 
			
		||||
		if exp.is_odd() {
 | 
			
		||||
			result = (result * &base) % &modulus;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		exp = exp >> 1;
 | 
			
		||||
		base = (base.clone() * base) % &modulus;
 | 
			
		||||
	// Left-to-right binary exponentiation (Handbook of Applied Cryptography - Algorithm 14.79).
 | 
			
		||||
	// http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
 | 
			
		||||
	let mut result = BigUint::one();
 | 
			
		||||
 | 
			
		||||
	for digit in exp {
 | 
			
		||||
		let mut mask = 1 << (BITS_PER_DIGIT - 1);
 | 
			
		||||
 | 
			
		||||
		for _ in 0..BITS_PER_DIGIT {
 | 
			
		||||
			result = &result * &result % &modulus;
 | 
			
		||||
 | 
			
		||||
			if digit & mask > 0 {
 | 
			
		||||
				result = result * &base % &modulus;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mask >>= 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -366,15 +382,19 @@ impl Impl for ModexpImpl {
 | 
			
		||||
		} else {
 | 
			
		||||
			// read the numbers themselves.
 | 
			
		||||
			let mut buf = vec![0; max(mod_len, max(base_len, exp_len))];
 | 
			
		||||
			let mut read_num = |len| {
 | 
			
		||||
			let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| {
 | 
			
		||||
				reader.read_exact(&mut buf[..len]).expect("reading from zero-extended memory cannot fail; qed");
 | 
			
		||||
				BigUint::from_bytes_be(&buf[..len])
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			let base = read_num(base_len);
 | 
			
		||||
			let exp = read_num(exp_len);
 | 
			
		||||
			let modulus = read_num(mod_len);
 | 
			
		||||
			modexp(base, exp, modulus)
 | 
			
		||||
			let base = read_num(&mut reader, base_len);
 | 
			
		||||
 | 
			
		||||
			let mut exp_buf = vec![0; exp_len];
 | 
			
		||||
			reader.read_exact(&mut exp_buf[..exp_len]).expect("reading from zero-extended memory cannot fail; qed");
 | 
			
		||||
 | 
			
		||||
			let modulus = read_num(&mut reader, mod_len);
 | 
			
		||||
 | 
			
		||||
			modexp(base, exp_buf, modulus)
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		// write output to given memory, left padded and same length as the modulus.
 | 
			
		||||
@ -551,31 +571,31 @@ mod tests {
 | 
			
		||||
		let mut base = BigUint::parse_bytes(b"12345", 10).unwrap();
 | 
			
		||||
		let mut exp = BigUint::zero();
 | 
			
		||||
		let mut modulus = BigUint::parse_bytes(b"789", 10).unwrap();
 | 
			
		||||
		assert_eq!(me(base, exp, modulus), BigUint::one());
 | 
			
		||||
		assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::one());
 | 
			
		||||
 | 
			
		||||
		// 0^n % m == 0
 | 
			
		||||
		base = BigUint::zero();
 | 
			
		||||
		exp = BigUint::parse_bytes(b"12345", 10).unwrap();
 | 
			
		||||
		modulus = BigUint::parse_bytes(b"789", 10).unwrap();
 | 
			
		||||
		assert_eq!(me(base, exp, modulus), BigUint::zero());
 | 
			
		||||
		assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero());
 | 
			
		||||
 | 
			
		||||
		// n^m % 1 == 0
 | 
			
		||||
		base = BigUint::parse_bytes(b"12345", 10).unwrap();
 | 
			
		||||
		exp = BigUint::parse_bytes(b"789", 10).unwrap();
 | 
			
		||||
		modulus = BigUint::one();
 | 
			
		||||
		assert_eq!(me(base, exp, modulus), BigUint::zero());
 | 
			
		||||
		assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero());
 | 
			
		||||
 | 
			
		||||
		// if n % d == 0, then n^m % d == 0
 | 
			
		||||
		base = BigUint::parse_bytes(b"12345", 10).unwrap();
 | 
			
		||||
		exp = BigUint::parse_bytes(b"789", 10).unwrap();
 | 
			
		||||
		modulus = BigUint::parse_bytes(b"15", 10).unwrap();
 | 
			
		||||
		assert_eq!(me(base, exp, modulus), BigUint::zero());
 | 
			
		||||
		assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::zero());
 | 
			
		||||
 | 
			
		||||
		// others
 | 
			
		||||
		base = BigUint::parse_bytes(b"12345", 10).unwrap();
 | 
			
		||||
		exp = BigUint::parse_bytes(b"789", 10).unwrap();
 | 
			
		||||
		modulus = BigUint::parse_bytes(b"97", 10).unwrap();
 | 
			
		||||
		assert_eq!(me(base, exp, modulus), BigUint::parse_bytes(b"55", 10).unwrap());
 | 
			
		||||
		assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::parse_bytes(b"55", 10).unwrap());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user