From 9e779ab7158afc0d58cedbd71be1e5806f3deaee Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 30 Jul 2020 15:52:16 +0200 Subject: [PATCH] pallet-evm: add builtin support for the four basic Ethereum precompiles (#6743) * pallet-evm: add builtin support for the four basic Ethereum precompiles * linear_cost -> ensure_linear_cost to directly return OutOfGas error --- Cargo.lock | 12 +++++ frame/evm/Cargo.toml | 2 + frame/evm/src/lib.rs | 2 +- frame/evm/src/precompiles.rs | 99 +++++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 574e4f4802550..710ce8d7b30b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4391,6 +4391,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "primitive-types", + "ripemd160", "rlp", "serde", "sha3", @@ -5954,6 +5955,17 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "rle-decode-fast" version = "1.0.1" diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index a5ec28ddf9c3b..43ecc6f3688cf 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -27,6 +27,7 @@ rlp = { version = "0.4", default-features = false } evm = { version = "0.17", default-features = false } sha3 = { version = "0.8", default-features = false } impl-trait-for-tuples = "0.1" +ripemd160 = { version = "0.9", default-features = false } [features] default = ["std"] @@ -45,4 +46,5 @@ std = [ "primitive-types/std", "evm/std", "pallet-timestamp/std", + "ripemd160/std", ] diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 0cbeac6fe2d6f..0dcc4526c7fbc 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -21,8 +21,8 @@ #![cfg_attr(not(feature = "std"), no_std)] mod backend; -mod precompiles; mod tests; +pub mod precompiles; pub use crate::precompiles::{Precompile, Precompiles}; pub use crate::backend::{Account, Log, Vicinity, Backend}; diff --git a/frame/evm/src/precompiles.rs b/frame/evm/src/precompiles.rs index a6a10d45a2083..987724285d7b8 100644 --- a/frame/evm/src/precompiles.rs +++ b/frame/evm/src/precompiles.rs @@ -15,9 +15,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sp_std::vec::Vec; +//! Builtin precompiles. + +use sp_std::{cmp::min, vec::Vec}; use sp_core::H160; use evm::{ExitError, ExitSucceed}; +use ripemd160::Digest; use impl_trait_for_tuples::impl_for_tuples; /// Custom precompiles to be used by EVM engine. @@ -67,3 +70,97 @@ impl Precompiles for Tuple { None } } + +/// Linear gas cost +fn ensure_linear_cost( + target_gas: Option, + len: usize, + base: usize, + word: usize +) -> Result { + let cost = base.checked_add( + word.checked_mul(len.saturating_add(31) / 32).ok_or(ExitError::OutOfGas)? + ).ok_or(ExitError::OutOfGas)?; + + if let Some(target_gas) = target_gas { + if cost > target_gas { + return Err(ExitError::OutOfGas) + } + } + + Ok(cost) +} + +/// The identity precompile. +pub struct Identity; + +impl Precompile for Identity { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 15, 3)?; + + Ok((ExitSucceed::Returned, input.to_vec(), cost)) + } +} + +/// The ecrecover precompile. +pub struct ECRecover; + +impl Precompile for ECRecover { + fn execute( + i: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, i.len(), 3000, 0)?; + + let mut input = [0u8; 128]; + input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); + + let mut msg = [0u8; 32]; + let mut sig = [0u8; 65]; + + msg[0..32].copy_from_slice(&input[0..32]); + sig[0..32].copy_from_slice(&input[64..96]); + sig[32..64].copy_from_slice(&input[96..128]); + sig[64] = input[63]; + + let pubkey = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) + .map_err(|_| ExitError::Other("Public key recover failed"))?; + let mut address = sp_io::hashing::keccak_256(&pubkey); + address[0..12].copy_from_slice(&[0u8; 12]); + + Ok((ExitSucceed::Returned, address.to_vec(), cost)) + } +} + +/// The ripemd precompile. +pub struct Ripemd160; + +impl Precompile for Ripemd160 { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 600, 120)?; + + let ret = ripemd160::Ripemd160::digest(input).to_vec(); + Ok((ExitSucceed::Returned, ret, cost)) + } +} + +/// The sha256 precompile. +pub struct Sha256; + +impl Precompile for Sha256 { + fn execute( + input: &[u8], + target_gas: Option, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let cost = ensure_linear_cost(target_gas, input.len(), 60, 12)?; + + let ret = sp_io::hashing::sha2_256(input); + Ok((ExitSucceed::Returned, ret.to_vec(), cost)) + } +}