From 25a9c9d760584c492741056219065035a41c2652 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 16 Dec 2020 19:52:48 +0100 Subject: [PATCH] Add precompile support for Substrate dispatchables (#246) --- Cargo.lock | 29 +++++++--- Cargo.toml | 1 + frame/ethereum/Cargo.toml | 2 +- frame/evm/Cargo.toml | 6 +-- frame/evm/precompile/blake2/Cargo.toml | 2 +- frame/evm/precompile/bn128/Cargo.toml | 2 +- frame/evm/precompile/dispatch/Cargo.toml | 30 +++++++++++ frame/evm/precompile/dispatch/src/lib.rs | 69 ++++++++++++++++++++++++ frame/evm/precompile/ed25519/Cargo.toml | 2 +- frame/evm/precompile/modexp/Cargo.toml | 2 +- frame/evm/precompile/simple/Cargo.toml | 2 +- frame/evm/src/runner/builtin.rs | 4 +- primitives/evm/Cargo.toml | 2 +- primitives/evm/src/precompile.rs | 8 ++- 14 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 frame/evm/precompile/dispatch/Cargo.toml create mode 100644 frame/evm/precompile/dispatch/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7faf386cb..8d1537621 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1085,9 +1085,9 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "evm" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0da85b407262b8caaffec7c5cb04c255538afdc1245de482d6216580d1f5e2" +checksum = "52494176f51e37ac07f10f0368a162391a3c368d6c859b69d4ebc52cfc090952" dependencies = [ "ethereum", "evm-core", @@ -1103,9 +1103,9 @@ dependencies = [ [[package]] name = "evm-core" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "decb1397cbc7c7e3c3fee6564eed1f294612a0365c66c90ab92726d19d253a6e" +checksum = "cad7ff84008aa24272cf3e9f7a71054271ede93613665cd9fdfa65447ae6e017" dependencies = [ "parity-scale-codec", "primitive-types", @@ -1114,9 +1114,9 @@ dependencies = [ [[package]] name = "evm-gasometer" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da079283764366124ee955f0bd049e691c3d00895e88c79d9c3b744ff4cd6595" +checksum = "8aed6fc83d542d58a63b7827bd9e4808e1e2a9324b503c255f9e2acccfa0bef9" dependencies = [ "evm-core", "evm-runtime", @@ -1125,9 +1125,9 @@ dependencies = [ [[package]] name = "evm-runtime" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95524d03dfcd11ca540fa3481d76dcc891db1abf704c2bec7a67fc51cdf95e67" +checksum = "507a6d0c3377f34335a264b387e13dc56c7946c34073e0c1250b923018bdf3fa" dependencies = [ "evm-core", "primitive-types", @@ -3733,6 +3733,19 @@ dependencies = [ "substrate-bn", ] +[[package]] +name = "pallet-evm-precompile-dispatch" +version = "2.0.0" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "pallet-evm", + "parity-scale-codec", + "sp-core", + "sp-io", +] + [[package]] name = "pallet-evm-precompile-ed25519" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index c67f32bcd..ea3d1b014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "frame/evm/precompile/ed25519", "frame/evm/precompile/bn128", "frame/evm/precompile/blake2", + "frame/evm/precompile/dispatch", "client/consensus", "client/rpc-core", "client/rpc", diff --git a/frame/ethereum/Cargo.toml b/frame/ethereum/Cargo.toml index e376b1f82..441183ac1 100644 --- a/frame/ethereum/Cargo.toml +++ b/frame/ethereum/Cargo.toml @@ -19,7 +19,7 @@ sp-runtime = { version = "2.0.0-dev", default-features = false, git = "https://g sp-std = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../primitives/evm" } -evm = { version = "0.19.0", features = ["with-codec"], default-features = false } +evm = { version = "0.20.0", features = ["with-codec"], default-features = false } ethereum = { version = "0.5", default-features = false, features = ["with-codec"] } ethereum-types = { version = "0.9", default-features = false } rlp = { version = "0.4", default-features = false } diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index 0cd0ed50b..b22d68a0d 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -26,9 +26,9 @@ sp-io = { version = "2.0.0", default-features = false, git = "https://github.com fp-evm = { version = "0.8.0", default-features = false, path = "../../primitives/evm" } primitive-types = { version = "0.7.0", default-features = false, features = ["rlp", "byteorder"] } rlp = { version = "0.4", default-features = false } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } -evm-runtime = { version = "0.19.0", default-features = false } -evm-gasometer = { version = "0.19.0", default-features = false } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } +evm-runtime = { version = "0.20.0", default-features = false } +evm-gasometer = { version = "0.20.0", default-features = false } sha3 = { version = "0.8", default-features = false } [features] diff --git a/frame/evm/precompile/blake2/Cargo.toml b/frame/evm/precompile/blake2/Cargo.toml index 6153e796d..c1733bb15 100644 --- a/frame/evm/precompile/blake2/Cargo.toml +++ b/frame/evm/precompile/blake2/Cargo.toml @@ -12,7 +12,7 @@ description = "BLAKE2 precompiles for EVM pallet." sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } [features] default = ["std"] diff --git a/frame/evm/precompile/bn128/Cargo.toml b/frame/evm/precompile/bn128/Cargo.toml index 9b95eced2..91be932be 100644 --- a/frame/evm/precompile/bn128/Cargo.toml +++ b/frame/evm/precompile/bn128/Cargo.toml @@ -12,7 +12,7 @@ description = "BN128 precompiles for EVM pallet." sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } bn = { package = "substrate-bn", version = "0.5", default-features = false } [features] diff --git a/frame/evm/precompile/dispatch/Cargo.toml b/frame/evm/precompile/dispatch/Cargo.toml new file mode 100644 index 000000000..f99769614 --- /dev/null +++ b/frame/evm/precompile/dispatch/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "pallet-evm-precompile-dispatch" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "DISPATCH precompiles for EVM pallet." + +[dependencies] +sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } +sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } +frame-support = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } +pallet-evm = { version = "2.0.0", default-features = false, path = "../.." } +fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } +codec = { package = "parity-scale-codec", version = "1.3.5", default-features = false } + +[features] +default = ["std"] +std = [ + "sp-core/std", + "sp-io/std", + "frame-support/std", + "pallet-evm/std", + "fp-evm/std", + "evm/std", + "codec/std", +] \ No newline at end of file diff --git a/frame/evm/precompile/dispatch/src/lib.rs b/frame/evm/precompile/dispatch/src/lib.rs new file mode 100644 index 000000000..eb8fbb495 --- /dev/null +++ b/frame/evm/precompile/dispatch/src/lib.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; +use core::marker::PhantomData; +use fp_evm::Precompile; +use evm::{ExitSucceed, ExitError, Context}; +use frame_support::{dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, weights::{Pays, DispatchClass}}; +use pallet_evm::AddressMapping; +use codec::Decode; + +pub struct Dispatch { + _marker: PhantomData, +} + +impl Precompile for Dispatch where + T: pallet_evm::Config, + T::Call: Dispatchable + GetDispatchInfo + Decode, + ::Origin: From>, +{ + fn execute( + input: &[u8], + target_gas: Option, + context: &Context, + ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { + let call = T::Call::decode(&mut &input[..]).map_err(|_| ExitError::Other("decode failed".into()))?; + let info = call.get_dispatch_info(); + + let valid_call = info.pays_fee == Pays::Yes && info.class == DispatchClass::Normal; + if !valid_call { + return Err(ExitError::Other("invalid call".into())) + } + + if let Some(gas) = target_gas { + let valid_weight = info.weight <= gas as u64; + if !valid_weight { + return Err(ExitError::OutOfGas) + } + } + + let origin = T::AddressMapping::into_account_id(context.caller); + + match call.dispatch(Some(origin).into()) { + Ok(post_info) => { + let cost = post_info.actual_weight.unwrap_or(info.weight) as usize; + Ok((ExitSucceed::Stopped, Default::default(), cost)) + }, + Err(_) => Err(ExitError::Other("dispatch execution failed".into())), + } + } +} diff --git a/frame/evm/precompile/ed25519/Cargo.toml b/frame/evm/precompile/ed25519/Cargo.toml index f4b420c20..1cfb08e38 100644 --- a/frame/evm/precompile/ed25519/Cargo.toml +++ b/frame/evm/precompile/ed25519/Cargo.toml @@ -12,7 +12,7 @@ description = "ED25519 precompiles for EVM pallet." sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } ed25519-dalek = { version = "1.0.0", features = ["alloc", "u64_backend"], default-features = false } [features] diff --git a/frame/evm/precompile/modexp/Cargo.toml b/frame/evm/precompile/modexp/Cargo.toml index c256754f6..48caf4323 100644 --- a/frame/evm/precompile/modexp/Cargo.toml +++ b/frame/evm/precompile/modexp/Cargo.toml @@ -12,7 +12,7 @@ description = "MODEXP precompiles for EVM pallet." sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } num = { version = "0.3", features = ["alloc"], default-features = false } [features] diff --git a/frame/evm/precompile/simple/Cargo.toml b/frame/evm/precompile/simple/Cargo.toml index 8a8662ba3..4d753d1e9 100644 --- a/frame/evm/precompile/simple/Cargo.toml +++ b/frame/evm/precompile/simple/Cargo.toml @@ -12,7 +12,7 @@ description = "Simple precompiles for EVM pallet." sp-core = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } sp-io = { version = "2.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "frontier" } fp-evm = { version = "0.8.0", default-features = false, path = "../../../../primitives/evm" } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } ripemd160 = { version = "0.9", default-features = false } [features] diff --git a/frame/evm/src/runner/builtin.rs b/frame/evm/src/runner/builtin.rs index e90e1ef84..4fc4f5fca 100644 --- a/frame/evm/src/runner/builtin.rs +++ b/frame/evm/src/runner/builtin.rs @@ -286,7 +286,7 @@ pub struct Handler<'vicinity, 'config, T: Config> { gasometer: Gasometer<'config>, deleted: BTreeSet, logs: Vec, - precompile: fn(H160, &[u8], Option) -> + precompile: fn(H160, &[u8], Option, &Context) -> Option, usize), ExitError>>, is_static: bool, _marker: PhantomData, @@ -299,7 +299,7 @@ impl<'vicinity, 'config, T: Config> Handler<'vicinity, 'config, T> { gas_limit: usize, is_static: bool, config: &'config EvmConfig, - precompile: fn(H160, &[u8], Option) -> + precompile: fn(H160, &[u8], Option, &Context) -> Option, usize), ExitError>>, ) -> Self { Self { diff --git a/primitives/evm/Cargo.toml b/primitives/evm/Cargo.toml index 7f63c76a4..7e8bac49d 100644 --- a/primitives/evm/Cargo.toml +++ b/primitives/evm/Cargo.toml @@ -18,7 +18,7 @@ sp-core = { version = "2.0.0", git = "https://github.com/paritytech/substrate.gi sp-std = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", branch = "frontier", default-features = false } serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -evm = { version = "0.19.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.20.0", default-features = false, features = ["with-codec"] } impl-trait-for-tuples = "0.1" [features] diff --git a/primitives/evm/src/precompile.rs b/primitives/evm/src/precompile.rs index a3ef719a4..9cd29c4ae 100644 --- a/primitives/evm/src/precompile.rs +++ b/primitives/evm/src/precompile.rs @@ -18,7 +18,7 @@ use sp_std::vec::Vec; use sp_core::H160; use impl_trait_for_tuples::impl_for_tuples; -use evm::{ExitSucceed, ExitError}; +use evm::{ExitSucceed, ExitError, Context}; /// Custom precompiles to be used by EVM engine. pub trait PrecompileSet { @@ -31,6 +31,7 @@ pub trait PrecompileSet { address: H160, input: &[u8], target_gas: Option, + context: &Context, ) -> Option, usize), ExitError>>; } @@ -42,6 +43,7 @@ pub trait Precompile { fn execute( input: &[u8], target_gas: Option, + context: &Context, ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError>; } @@ -54,13 +56,14 @@ impl PrecompileSet for Tuple { address: H160, input: &[u8], target_gas: Option, + context: &Context, ) -> Option, usize), ExitError>> { let mut index = 0; for_tuples!( #( index += 1; if address == H160::from_low_u64_be(index) { - return Some(Tuple::execute(input, target_gas)) + return Some(Tuple::execute(input, target_gas, context)) } )* ); @@ -82,6 +85,7 @@ impl Precompile for T { fn execute( input: &[u8], target_gas: Option, + _: &Context, ) -> core::result::Result<(ExitSucceed, Vec, usize), ExitError> { let cost = ensure_linear_cost(target_gas, input.len(), T::BASE, T::WORD)?;