pallet-evm: add support for transaction-level create2 (#4907)

* pallet-evm: add support for transaction-level create2

* Bump runtime version

* Switch to FunctionOf for weights
This commit is contained in:
Wei Tang
2020-02-13 19:12:26 +01:00
committed by GitHub
parent 6e31cd7af7
commit 0f122445f6
4 changed files with 88 additions and 47 deletions
+8 -8
View File
@@ -1235,9 +1235,9 @@ dependencies = [
[[package]]
name = "evm"
version = "0.14.2"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f887b371f9999682ccc5b1cb771e7d4408ae61e93fc0343ceaeb761fca42d1"
checksum = "272f65e18a2b6449b682bfcdf6c3ccc63db0b93898d89c0fb237548bbfc764a5"
dependencies = [
"evm-core",
"evm-gasometer",
@@ -1250,18 +1250,18 @@ dependencies = [
[[package]]
name = "evm-core"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcde5af3d542874ddeb53de0919302d57586ea04b3f76f54d865f8a6cdc70ae"
checksum = "66534d42e13d50f9101bed87cb568fd5aa929c600c3c13f8dadbbf39f5635945"
dependencies = [
"primitive-types",
]
[[package]]
name = "evm-gasometer"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b82bc9f275cb59d2bcc05d85c98736ddfaba003a7ef7b73893fa7c1c1fab29dc"
checksum = "39bc5b592803ca644781fe2290b7305ea5182f7c9516d615ddfb2308c2cab639"
dependencies = [
"evm-core",
"evm-runtime",
@@ -1270,9 +1270,9 @@ dependencies = [
[[package]]
name = "evm-runtime"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dbbc89d29618c3722c17ba78ddf432d40ace8ee27e3f8b28b52a85921112e4b"
checksum = "389e4b447fb26971a9c76c8aa49c0ab435f8e46e8fc46e1bc4ebf01f3c2b428f"
dependencies = [
"evm-core",
"primitive-types",
+2 -2
View File
@@ -82,8 +82,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to 0. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 216,
impl_version: 3,
spec_version: 217,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
};
+1 -1
View File
@@ -18,7 +18,7 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives
sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" }
primitive-types = { version = "0.6.2", default-features = false, features = ["rlp"] }
rlp = { version = "0.4", default-features = false }
evm = { version = "0.14", default-features = false }
evm = { version = "0.15", default-features = false }
sha3 = { version = "0.8", default-features = false }
[features]
+77 -36
View File
@@ -25,7 +25,7 @@ pub use crate::backend::{Account, Log, Vicinity, Backend};
use sp_std::{vec::Vec, marker::PhantomData};
use frame_support::{ensure, decl_module, decl_storage, decl_event, decl_error};
use frame_support::weights::{Weight, WeighData, ClassifyDispatch, DispatchClass, PaysFee};
use frame_support::weights::{Weight, DispatchClass, FunctionOf};
use frame_support::traits::{Currency, WithdrawReason, ExistenceRequirement};
use frame_system::{self as system, ensure_signed};
use sp_runtime::ModuleId;
@@ -34,6 +34,7 @@ use sp_core::{U256, H256, H160, Hasher};
use sp_runtime::{
DispatchResult, traits::{UniqueSaturatedInto, AccountIdConversion, SaturatedConversion},
};
use sha3::{Digest, Keccak256};
use evm::{ExitReason, ExitSucceed, ExitError};
use evm::executor::StackExecutor;
use evm::backend::ApplyBackend;
@@ -115,38 +116,6 @@ impl Precompiles for () {
}
}
struct WeightForCallCreate;
impl WeighData<(&H160, &Vec<u8>, &U256, &u32, &U256, &Option<U256>)> for WeightForCallCreate {
fn weigh_data(
&self,
(_, _, _, gas_provided, gas_price, _): (&H160, &Vec<u8>, &U256, &u32, &U256, &Option<U256>)
) -> Weight {
(*gas_price).saturated_into::<Weight>().saturating_mul(*gas_provided)
}
}
impl WeighData<(&Vec<u8>, &U256, &u32, &U256, &Option<U256>)> for WeightForCallCreate {
fn weigh_data(
&self,
(_, _, gas_provided, gas_price, _): (&Vec<u8>, &U256, &u32, &U256, &Option<U256>)
) -> Weight {
(*gas_price).saturated_into::<Weight>().saturating_mul(*gas_provided)
}
}
impl<T> ClassifyDispatch<T> for WeightForCallCreate {
fn classify_dispatch(&self, _: T) -> DispatchClass {
DispatchClass::Normal
}
}
impl<T> PaysFee<T> for WeightForCallCreate {
fn pays_fee(&self, _: T) -> bool {
true
}
}
/// EVM module trait
pub trait Trait: frame_system::Trait + pallet_timestamp::Trait {
/// Calculator for current gas price.
@@ -252,7 +221,7 @@ decl_module! {
}
/// Issue an EVM call operation. This is similar to a message call transaction in Ethereum.
#[weight = WeightForCallCreate]
#[weight = FunctionOf(|(_, _, _, gas_limit, gas_price, _): (&H160, &Vec<u8>, &U256, &u32, &U256, &Option<U256>)| (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit), DispatchClass::Normal, true)]
fn call(
origin,
target: H160,
@@ -315,7 +284,7 @@ decl_module! {
/// Issue an EVM create operation. This is similar to a contract creation transaction in
/// Ethereum.
#[weight = WeightForCallCreate]
#[weight = FunctionOf(|(_, _, gas_limit, gas_price, _): (&Vec<u8>, &U256, &u32, &U256, &Option<U256>)| (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit), DispatchClass::Normal, true)]
fn create(
origin,
init: Vec<u8>,
@@ -353,7 +322,9 @@ decl_module! {
ensure!(source_account.nonce == nonce, Error::<T>::InvalidNonce);
}
let create_address = executor.create_address(source, evm::CreateScheme::Dynamic);
let create_address = executor.create_address(
evm::CreateScheme::Legacy { caller: source }
);
let reason = executor.transact_create(
source,
value,
@@ -378,6 +349,76 @@ decl_module! {
ret.map_err(Into::into)
}
/// Issue an EVM create2 operation.
#[weight = FunctionOf(|(_, _, _, gas_limit, gas_price, _): (&Vec<u8>, &H256, &U256, &u32, &U256, &Option<U256>)| (*gas_price).saturated_into::<Weight>().saturating_mul(*gas_limit), DispatchClass::Normal, true)]
fn create2(
origin,
init: Vec<u8>,
salt: H256,
value: U256,
gas_limit: u32,
gas_price: U256,
nonce: Option<U256>,
) -> DispatchResult {
let sender = ensure_signed(origin)?;
ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::<T>::GasPriceTooLow);
let source = T::ConvertAccountId::convert_account_id(&sender);
let vicinity = Vicinity {
gas_price,
origin: source,
};
let mut backend = Backend::<T>::new(&vicinity);
let mut executor = StackExecutor::new_with_precompile(
&backend,
gas_limit as usize,
&backend::GASOMETER_CONFIG,
T::Precompiles::execute,
);
let total_fee = gas_price.checked_mul(U256::from(gas_limit))
.ok_or(Error::<T>::FeeOverflow)?;
let total_payment = value.checked_add(total_fee).ok_or(Error::<T>::PaymentOverflow)?;
let source_account = Accounts::get(&source);
ensure!(source_account.balance >= total_payment, Error::<T>::BalanceLow);
executor.withdraw(source, total_fee).map_err(|_| Error::<T>::WithdrawFailed)?;
if let Some(nonce) = nonce {
ensure!(source_account.nonce == nonce, Error::<T>::InvalidNonce);
}
let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice());
let create_address = executor.create_address(
evm::CreateScheme::Create2 { caller: source, code_hash, salt }
);
let reason = executor.transact_create2(
source,
value,
init,
salt,
gas_limit as usize,
);
let ret = match reason {
ExitReason::Succeed(_) => {
Module::<T>::deposit_event(Event::Created(create_address));
Ok(())
},
ExitReason::Error(_) => Err(Error::<T>::ExitReasonFailed),
ExitReason::Revert(_) => Err(Error::<T>::ExitReasonRevert),
ExitReason::Fatal(_) => Err(Error::<T>::ExitReasonFatal),
};
let actual_fee = executor.fee(gas_price);
executor.deposit(source, total_fee.saturating_sub(actual_fee));
let (values, logs) = executor.deconstruct();
backend.apply(values, logs, true);
ret.map_err(Into::into)
}
}
}