Skip to content

Commit 4342a4b

Browse files
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
1 parent 74b063c commit 4342a4b

File tree

2 files changed

+78
-37
lines changed

2 files changed

+78
-37
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives
1818
sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" }
1919
primitive-types = { version = "0.6.2", default-features = false, features = ["rlp"] }
2020
rlp = { version = "0.4", default-features = false }
21-
evm = { version = "0.14", default-features = false }
21+
evm = { version = "0.15", default-features = false }
2222
sha3 = { version = "0.8", default-features = false }
2323

2424
[features]

src/lib.rs

Lines changed: 77 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub use crate::backend::{Account, Log, Vicinity, Backend};
2525

2626
use sp_std::{vec::Vec, marker::PhantomData};
2727
use frame_support::{ensure, decl_module, decl_storage, decl_event, decl_error};
28-
use frame_support::weights::{Weight, WeighData, ClassifyDispatch, DispatchClass, PaysFee};
28+
use frame_support::weights::{Weight, DispatchClass, FunctionOf};
2929
use frame_support::traits::{Currency, WithdrawReason, ExistenceRequirement};
3030
use frame_system::{self as system, ensure_signed};
3131
use sp_runtime::ModuleId;
@@ -34,6 +34,7 @@ use sp_core::{U256, H256, H160, Hasher};
3434
use sp_runtime::{
3535
DispatchResult, traits::{UniqueSaturatedInto, AccountIdConversion, SaturatedConversion},
3636
};
37+
use sha3::{Digest, Keccak256};
3738
use evm::{ExitReason, ExitSucceed, ExitError};
3839
use evm::executor::StackExecutor;
3940
use evm::backend::ApplyBackend;
@@ -115,38 +116,6 @@ impl Precompiles for () {
115116
}
116117
}
117118

118-
struct WeightForCallCreate;
119-
120-
impl WeighData<(&H160, &Vec<u8>, &U256, &u32, &U256, &Option<U256>)> for WeightForCallCreate {
121-
fn weigh_data(
122-
&self,
123-
(_, _, _, gas_provided, gas_price, _): (&H160, &Vec<u8>, &U256, &u32, &U256, &Option<U256>)
124-
) -> Weight {
125-
(*gas_price).saturated_into::<Weight>().saturating_mul(*gas_provided)
126-
}
127-
}
128-
129-
impl WeighData<(&Vec<u8>, &U256, &u32, &U256, &Option<U256>)> for WeightForCallCreate {
130-
fn weigh_data(
131-
&self,
132-
(_, _, gas_provided, gas_price, _): (&Vec<u8>, &U256, &u32, &U256, &Option<U256>)
133-
) -> Weight {
134-
(*gas_price).saturated_into::<Weight>().saturating_mul(*gas_provided)
135-
}
136-
}
137-
138-
impl<T> ClassifyDispatch<T> for WeightForCallCreate {
139-
fn classify_dispatch(&self, _: T) -> DispatchClass {
140-
DispatchClass::Normal
141-
}
142-
}
143-
144-
impl<T> PaysFee<T> for WeightForCallCreate {
145-
fn pays_fee(&self, _: T) -> bool {
146-
true
147-
}
148-
}
149-
150119
/// EVM module trait
151120
pub trait Trait: frame_system::Trait + pallet_timestamp::Trait {
152121
/// Calculator for current gas price.
@@ -252,7 +221,7 @@ decl_module! {
252221
}
253222

254223
/// Issue an EVM call operation. This is similar to a message call transaction in Ethereum.
255-
#[weight = WeightForCallCreate]
224+
#[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)]
256225
fn call(
257226
origin,
258227
target: H160,
@@ -315,7 +284,7 @@ decl_module! {
315284

316285
/// Issue an EVM create operation. This is similar to a contract creation transaction in
317286
/// Ethereum.
318-
#[weight = WeightForCallCreate]
287+
#[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)]
319288
fn create(
320289
origin,
321290
init: Vec<u8>,
@@ -353,7 +322,9 @@ decl_module! {
353322
ensure!(source_account.nonce == nonce, Error::<T>::InvalidNonce);
354323
}
355324

356-
let create_address = executor.create_address(source, evm::CreateScheme::Dynamic);
325+
let create_address = executor.create_address(
326+
evm::CreateScheme::Legacy { caller: source }
327+
);
357328
let reason = executor.transact_create(
358329
source,
359330
value,
@@ -378,6 +349,76 @@ decl_module! {
378349

379350
ret.map_err(Into::into)
380351
}
352+
353+
/// Issue an EVM create2 operation.
354+
#[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)]
355+
fn create2(
356+
origin,
357+
init: Vec<u8>,
358+
salt: H256,
359+
value: U256,
360+
gas_limit: u32,
361+
gas_price: U256,
362+
nonce: Option<U256>,
363+
) -> DispatchResult {
364+
let sender = ensure_signed(origin)?;
365+
ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::<T>::GasPriceTooLow);
366+
367+
let source = T::ConvertAccountId::convert_account_id(&sender);
368+
369+
let vicinity = Vicinity {
370+
gas_price,
371+
origin: source,
372+
};
373+
374+
let mut backend = Backend::<T>::new(&vicinity);
375+
let mut executor = StackExecutor::new_with_precompile(
376+
&backend,
377+
gas_limit as usize,
378+
&backend::GASOMETER_CONFIG,
379+
T::Precompiles::execute,
380+
);
381+
382+
let total_fee = gas_price.checked_mul(U256::from(gas_limit))
383+
.ok_or(Error::<T>::FeeOverflow)?;
384+
let total_payment = value.checked_add(total_fee).ok_or(Error::<T>::PaymentOverflow)?;
385+
let source_account = Accounts::get(&source);
386+
ensure!(source_account.balance >= total_payment, Error::<T>::BalanceLow);
387+
executor.withdraw(source, total_fee).map_err(|_| Error::<T>::WithdrawFailed)?;
388+
389+
if let Some(nonce) = nonce {
390+
ensure!(source_account.nonce == nonce, Error::<T>::InvalidNonce);
391+
}
392+
393+
let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice());
394+
let create_address = executor.create_address(
395+
evm::CreateScheme::Create2 { caller: source, code_hash, salt }
396+
);
397+
let reason = executor.transact_create2(
398+
source,
399+
value,
400+
init,
401+
salt,
402+
gas_limit as usize,
403+
);
404+
405+
let ret = match reason {
406+
ExitReason::Succeed(_) => {
407+
Module::<T>::deposit_event(Event::Created(create_address));
408+
Ok(())
409+
},
410+
ExitReason::Error(_) => Err(Error::<T>::ExitReasonFailed),
411+
ExitReason::Revert(_) => Err(Error::<T>::ExitReasonRevert),
412+
ExitReason::Fatal(_) => Err(Error::<T>::ExitReasonFatal),
413+
};
414+
let actual_fee = executor.fee(gas_price);
415+
executor.deposit(source, total_fee.saturating_sub(actual_fee));
416+
417+
let (values, logs) = executor.deconstruct();
418+
backend.apply(values, logs, true);
419+
420+
ret.map_err(Into::into)
421+
}
381422
}
382423
}
383424

0 commit comments

Comments
 (0)