diff --git a/src/precompiles/blake2.rs b/src/precompiles/blake2.rs index e0e8e3d89..d7186a552 100644 --- a/src/precompiles/blake2.rs +++ b/src/precompiles/blake2.rs @@ -15,6 +15,10 @@ mod consts { pub(super) struct Blake2F; +impl Blake2F { + pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 9); +} + impl Precompile for Blake2F { fn required_gas(input: &[u8]) -> Result { let (int_bytes, _) = input.split_at(mem::size_of::()); diff --git a/src/precompiles/bn128.rs b/src/precompiles/bn128.rs index 75dc53596..7b1a15200 100644 --- a/src/precompiles/bn128.rs +++ b/src/precompiles/bn128.rs @@ -41,6 +41,15 @@ mod consts { pub(super) const PAIR_ELEMENT_LEN: usize = 192; } +/// bn128 precompile addresses +pub(super) mod addresses { + use crate::precompiles; + + pub const ADD: [u8; 20] = precompiles::make_address(0, 6); + pub const MUL: [u8; 20] = precompiles::make_address(0, 7); + pub const PAIR: [u8; 20] = precompiles::make_address(0, 8); +} + /// Reads the `x` and `y` points from an input at a given position. fn read_point(input: &[u8], pos: usize) -> Result { use bn::{AffineG1, Fq, Group, G1}; diff --git a/src/precompiles/hash.rs b/src/precompiles/hash.rs index b9467ee7f..e8eb3bb5c 100644 --- a/src/precompiles/hash.rs +++ b/src/precompiles/hash.rs @@ -20,6 +20,10 @@ mod consts { /// SHA256 precompile. pub struct SHA256; +impl SHA256 { + pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 2); +} + impl Precompile for SHA256 { fn required_gas(input: &[u8]) -> Result { Ok( @@ -67,6 +71,10 @@ impl Precompile for SHA256 { /// RIPEMD160 precompile. pub struct RIPEMD160; +impl RIPEMD160 { + pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 3); +} + impl Precompile for RIPEMD160 { fn required_gas(input: &[u8]) -> Result { Ok( diff --git a/src/precompiles/identity.rs b/src/precompiles/identity.rs index bf7db352d..de41884e4 100644 --- a/src/precompiles/identity.rs +++ b/src/precompiles/identity.rs @@ -17,6 +17,10 @@ mod consts { pub struct Identity; +impl Identity { + pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 4); +} + impl Precompile for Identity { fn required_gas(input: &[u8]) -> Result { Ok( diff --git a/src/precompiles/mod.rs b/src/precompiles/mod.rs index 33667e17e..8a29218e6 100644 --- a/src/precompiles/mod.rs +++ b/src/precompiles/mod.rs @@ -17,18 +17,6 @@ use crate::precompiles::secp256k1::ECRecover; use crate::prelude::{Address, Vec}; use evm::{Context, ExitError, ExitSucceed}; -/// Exit to Ethereum precompile address (truncated to 8 bytes) -/// -/// Address: `0xb0bd02f6a392af548bdf1cfaee5dfa0eefcc8eab` -/// This address is computed as: `&keccak("exitToEthereum")[12..]` -const EXIT_TO_ETHEREUM_ID: u64 = 17176159495920586411; - -/// Exit to NEAR precompile address (truncated to 8 bytes) -/// -/// Address: `0xe9217bc70b7ed1f598ddd3199e80b093fa71124f` -/// This address is computed as: `&keccak("exitToNear")[12..]` -const EXIT_TO_NEAR_ID: u64 = 11421322804619973199; - /// A precompile operation result. type PrecompileResult = Result<(ExitSucceed, Vec, u64), ExitError>; @@ -88,12 +76,12 @@ pub fn homestead_precompiles( None => return Some(PrecompileResult::Err(ExitError::OutOfGas)), }; - match address.to_low_u64_be() { - 1 => Some(ECRecover::run(input, target_gas, context)), - 2 => Some(SHA256::run(input, target_gas, context)), - 3 => Some(RIPEMD160::run(input, target_gas, context)), - EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)), - EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)), + match address.0 { + ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)), + SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)), + RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)), + ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)), + ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)), _ => None, } } @@ -111,17 +99,17 @@ pub fn byzantium_precompiles( None => return Some(PrecompileResult::Err(ExitError::OutOfGas)), }; - match address.to_low_u64_be() { - 1 => Some(ECRecover::run(input, target_gas, context)), - 2 => Some(SHA256::run(input, target_gas, context)), - 3 => Some(RIPEMD160::run(input, target_gas, context)), - 4 => Some(Identity::run(input, target_gas, context)), - 5 => Some(ModExp::::run(input, target_gas, context)), - 6 => Some(BN128Add::::run(input, target_gas, context)), - 7 => Some(BN128Mul::::run(input, target_gas, context)), - 8 => Some(BN128Pair::::run(input, target_gas, context)), - EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)), - EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)), + match address.0 { + ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)), + SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)), + RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)), + Identity::ADDRESS => Some(Identity::run(input, target_gas, context)), + modexp::ADDRESS => Some(ModExp::::run(input, target_gas, context)), + bn128::addresses::ADD => Some(BN128Add::::run(input, target_gas, context)), + bn128::addresses::MUL => Some(BN128Mul::::run(input, target_gas, context)), + bn128::addresses::PAIR => Some(BN128Pair::::run(input, target_gas, context)), + ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)), + ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)), _ => None, } } @@ -139,18 +127,18 @@ pub fn istanbul_precompiles( None => return Some(PrecompileResult::Err(ExitError::OutOfGas)), }; - match address.to_low_u64_be() { - 1 => Some(ECRecover::run(input, target_gas, context)), - 2 => Some(SHA256::run(input, target_gas, context)), - 3 => Some(RIPEMD160::run(input, target_gas, context)), - 4 => Some(Identity::run(input, target_gas, context)), - 5 => Some(ModExp::::run(input, target_gas, context)), - 6 => Some(BN128Add::::run(input, target_gas, context)), - 7 => Some(BN128Mul::::run(input, target_gas, context)), - 8 => Some(BN128Pair::::run(input, target_gas, context)), - 9 => Some(Blake2F::run(input, target_gas, context)), - EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)), - EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)), + match address.0 { + ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)), + SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)), + RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)), + Identity::ADDRESS => Some(Identity::run(input, target_gas, context)), + modexp::ADDRESS => Some(ModExp::::run(input, target_gas, context)), + bn128::addresses::ADD => Some(BN128Add::::run(input, target_gas, context)), + bn128::addresses::MUL => Some(BN128Mul::::run(input, target_gas, context)), + bn128::addresses::PAIR => Some(BN128Pair::::run(input, target_gas, context)), + Blake2F::ADDRESS => Some(Blake2F::run(input, target_gas, context)), + ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)), + ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)), _ => None, } } @@ -168,20 +156,99 @@ pub fn berlin_precompiles( None => return Some(PrecompileResult::Err(ExitError::OutOfGas)), }; - match address.to_low_u64_be() { - 1 => Some(ECRecover::run(input, target_gas, context)), - 2 => Some(SHA256::run(input, target_gas, context)), - 3 => Some(RIPEMD160::run(input, target_gas, context)), - 4 => Some(Identity::run(input, target_gas, context)), - 5 => Some(ModExp::::run(input, target_gas, context)), // TODO gas changes - 6 => Some(BN128Add::::run(input, target_gas, context)), - 7 => Some(BN128Mul::::run(input, target_gas, context)), - 8 => Some(BN128Pair::::run(input, target_gas, context)), - 9 => Some(Blake2F::run(input, target_gas, context)), + match address.0 { + ECRecover::ADDRESS => Some(ECRecover::run(input, target_gas, context)), + SHA256::ADDRESS => Some(SHA256::run(input, target_gas, context)), + RIPEMD160::ADDRESS => Some(RIPEMD160::run(input, target_gas, context)), + Identity::ADDRESS => Some(Identity::run(input, target_gas, context)), + modexp::ADDRESS => Some(ModExp::::run(input, target_gas, context)), // TODO gas changes + bn128::addresses::ADD => Some(BN128Add::::run(input, target_gas, context)), + bn128::addresses::MUL => Some(BN128Mul::::run(input, target_gas, context)), + bn128::addresses::PAIR => Some(BN128Pair::::run(input, target_gas, context)), + Blake2F::ADDRESS => Some(Blake2F::run(input, target_gas, context)), #[cfg(feature = "contract")] - EXIT_TO_NEAR_ID => Some(ExitToNear::run(input, target_gas, context)), + ExitToNear::ADDRESS => Some(ExitToNear::run(input, target_gas, context)), #[cfg(feature = "contract")] - EXIT_TO_ETHEREUM_ID => Some(ExitToEthereum::run(input, target_gas, context)), + ExitToEthereum::ADDRESS => Some(ExitToEthereum::run(input, target_gas, context)), _ => None, } } + +/// const fn for making an address by concatenating the bytes from two given numbers, +/// Note that 32 + 128 = 160 = 20 bytes (the length of an address). This function is used +/// as a convenience for specifying the addresses of the various precompiles. +const fn make_address(x: u32, y: u128) -> [u8; 20] { + let x_bytes = x.to_be_bytes(); + let y_bytes = y.to_be_bytes(); + [ + x_bytes[0], + x_bytes[1], + x_bytes[2], + x_bytes[3], + y_bytes[0], + y_bytes[1], + y_bytes[2], + y_bytes[3], + y_bytes[4], + y_bytes[5], + y_bytes[6], + y_bytes[7], + y_bytes[8], + y_bytes[9], + y_bytes[10], + y_bytes[11], + y_bytes[12], + y_bytes[13], + y_bytes[14], + y_bytes[15], + ] +} + +#[cfg(test)] +mod tests { + use rand::Rng; + + #[test] + fn test_precompile_addresses() { + assert_eq!(super::secp256k1::ECRecover::ADDRESS, u8_to_address(1)); + assert_eq!(super::hash::SHA256::ADDRESS, u8_to_address(2)); + assert_eq!(super::hash::RIPEMD160::ADDRESS, u8_to_address(3)); + assert_eq!(super::identity::Identity::ADDRESS, u8_to_address(4)); + assert_eq!(super::modexp::ADDRESS, u8_to_address(5)); + assert_eq!(super::bn128::addresses::ADD, u8_to_address(6)); + assert_eq!(super::bn128::addresses::MUL, u8_to_address(7)); + assert_eq!(super::bn128::addresses::PAIR, u8_to_address(8)); + assert_eq!(super::blake2::Blake2F::ADDRESS, u8_to_address(9)); + } + + #[test] + fn test_make_address() { + for i in 0..u8::MAX { + assert_eq!(super::make_address(0, i as u128), u8_to_address(i)); + } + + let mut rng = rand::thread_rng(); + for _ in 0..u8::MAX { + let address: [u8; 20] = rng.gen(); + let (x, y) = split_address(address); + assert_eq!(address, super::make_address(x, y)) + } + } + + fn u8_to_address(x: u8) -> [u8; 20] { + let mut bytes = [0u8; 20]; + bytes[19] = x; + bytes + } + + // Inverse function of `super::make_address`. + fn split_address(a: [u8; 20]) -> (u32, u128) { + let mut x_bytes = [0u8; 4]; + let mut y_bytes = [0u8; 16]; + + x_bytes.copy_from_slice(&a[0..4]); + y_bytes.copy_from_slice(&a[4..20]); + + (u32::from_be_bytes(x_bytes), u128::from_be_bytes(y_bytes)) + } +} diff --git a/src/precompiles/modexp.rs b/src/precompiles/modexp.rs index 584e03419..102caf0ae 100644 --- a/src/precompiles/modexp.rs +++ b/src/precompiles/modexp.rs @@ -3,6 +3,8 @@ use crate::prelude::{PhantomData, Vec, U256}; use evm::{Context, ExitError, ExitSucceed}; use num::BigUint; +pub(super) const ADDRESS: [u8; 20] = super::make_address(0, 5); + pub(super) struct ModExp(PhantomData); impl ModExp { diff --git a/src/precompiles/native.rs b/src/precompiles/native.rs index df2bbd18b..33adcfcde 100644 --- a/src/precompiles/native.rs +++ b/src/precompiles/native.rs @@ -67,6 +67,15 @@ pub fn is_valid_account_id(account_id: &[u8]) -> bool { pub struct ExitToNear; //TransferEthToNear +impl ExitToNear { + /// Exit to NEAR precompile address + /// + /// Address: `0xe9217bc70b7ed1f598ddd3199e80b093fa71124f` + /// This address is computed as: `&keccak("exitToNear")[12..]` + pub(super) const ADDRESS: [u8; 20] = + super::make_address(0xe9217bc7, 0x0b7ed1f598ddd3199e80b093fa71124f); +} + impl Precompile for ExitToNear { fn required_gas(_input: &[u8]) -> Result { Ok(costs::EXIT_TO_NEAR_GAS) @@ -155,6 +164,15 @@ impl Precompile for ExitToNear { pub struct ExitToEthereum; +impl ExitToEthereum { + /// Exit to Ethereum precompile address + /// + /// Address: `0xb0bd02f6a392af548bdf1cfaee5dfa0eefcc8eab` + /// This address is computed as: `&keccak("exitToEthereum")[12..]` + pub(super) const ADDRESS: [u8; 20] = + super::make_address(0xb0bd02f6, 0xa392af548bdf1cfaee5dfa0eefcc8eab); +} + impl Precompile for ExitToEthereum { fn required_gas(_input: &[u8]) -> Result { Ok(costs::EXIT_TO_ETHEREUM_GAS) @@ -245,19 +263,18 @@ impl Precompile for ExitToEthereum { #[cfg(test)] mod tests { - use super::*; - use crate::precompiles::{EXIT_TO_ETHEREUM_ID, EXIT_TO_NEAR_ID}; + use super::{ExitToEthereum, ExitToNear}; use crate::types::near_account_to_evm_address; #[test] fn test_precompile_id() { assert_eq!( - EXIT_TO_ETHEREUM_ID, - near_account_to_evm_address("exitToEthereum".as_bytes()).to_low_u64_be() + ExitToEthereum::ADDRESS, + near_account_to_evm_address("exitToEthereum".as_bytes()).0 ); assert_eq!( - EXIT_TO_NEAR_ID, - near_account_to_evm_address("exitToNear".as_bytes()).to_low_u64_be() + ExitToNear::ADDRESS, + near_account_to_evm_address("exitToNear".as_bytes()).0 ); } } diff --git a/src/precompiles/secp256k1.rs b/src/precompiles/secp256k1.rs index 5a88f8c62..6cc2fc6f4 100644 --- a/src/precompiles/secp256k1.rs +++ b/src/precompiles/secp256k1.rs @@ -41,6 +41,10 @@ pub(crate) fn ecrecover(hash: H256, signature: &[u8]) -> Result Result { Ok(costs::ECRECOVER_BASE)