Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions modules/currencies/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ impl module_evm::Config for Runtime {

type Runner = module_evm::runner::stack::Runner<Self>;
type FindAuthor = ();
type Task = ();
type IdleScheduler = ();
type WeightInfo = ();
}

Expand Down
2 changes: 2 additions & 0 deletions modules/evm-bridge/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ impl module_evm::Config for Runtime {

type Runner = module_evm::runner::stack::Runner<Self>;
type FindAuthor = ();
type Task = ();
type IdleScheduler = ();
type WeightInfo = ();
}

Expand Down
2 changes: 2 additions & 0 deletions modules/evm-manager/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ impl module_evm::Config for Runtime {

type Runner = module_evm::runner::stack::Runner<Self>;
type FindAuthor = ();
type Task = ();
type IdleScheduler = ();
type WeightInfo = ();
}

Expand Down
3 changes: 2 additions & 1 deletion modules/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ module-evm-utiltity = { path = "../evm-utiltity", default-features = false }
primitives = { package = "acala-primitives", path = "../../primitives", default-features = false }

[dev-dependencies]
env_logger = "0.7"
env_logger = "0.9.0"
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.12" }
orml-currencies = { path = "../../orml/currencies" }
orml-tokens = { path = "../../orml/tokens" }
module-idle-scheduler = { path = "../idle-scheduler" }

[features]
default = ["std"]
Expand Down
200 changes: 159 additions & 41 deletions modules/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub use crate::{
Runner,
},
};
use codec::{Decode, Encode, MaxEncodedLen};
use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
use frame_support::{
dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo},
ensure,
Expand All @@ -55,27 +55,37 @@ pub use module_evm_utiltity::{
Account,
};
pub use module_support::{
AddressMapping, EVMStateRentTrait, ExecutionMode, InvokeContext, TransactionPayment, EVM as EVMTrait,
AddressMapping, DispatchableTask, EVMStateRentTrait, ExecutionMode, IdleScheduler, InvokeContext,
TransactionPayment, EVM as EVMTrait,
};
pub use orml_traits::currency::TransferAll;
use primitive_types::{H160, H256, U256};
pub use primitives::{
evm::{CallInfo, CreateInfo, EvmAddress, ExecutionInfo, Vicinity},
task::TaskResult,
ReserveIdentifier, H160_PREFIX_DEXSHARE, H160_PREFIX_TOKEN, MIRRORED_NFT_ADDRESS_START, PRECOMPILE_ADDRESS_START,
SYSTEM_CONTRACT_ADDRESS_PREFIX,
};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sha3::{Digest, Keccak256};
use sp_io::KillStorageResult::{AllRemoved, SomeRemaining};
use sp_runtime::{
traits::{
Convert, DispatchInfoOf, One, PostDispatchInfoOf, Saturating, SignedExtension, UniqueSaturatedInto, Zero,
},
transaction_validity::TransactionValidityError,
Either, TransactionOutcome,
};
use sp_std::{collections::btree_map::BTreeMap, convert::TryInto, fmt::Write, marker::PhantomData, prelude::*};
use sp_std::{
cmp,
collections::btree_map::BTreeMap,
convert::TryInto,
fmt::{Debug, Write},
marker::PhantomData,
prelude::*,
};

pub mod precompiles;
pub mod runner;
Expand Down Expand Up @@ -220,6 +230,12 @@ pub mod module {
/// Find author for the current block.
type FindAuthor: FindAuthor<Self::AccountId>;

/// Dispatchable tasks
type Task: DispatchableTask + FullCodec + Debug + Clone + PartialEq + TypeInfo + From<EvmTask<Self>>;

/// Idle scheduler for the evm task.
type IdleScheduler: IdleScheduler<Self::Task>;

/// Weight information for the extrinsics in this module.
type WeightInfo: WeightInfo;
}
Expand Down Expand Up @@ -366,8 +382,7 @@ pub mod module {
address: *address,
apparent_value: Default::default(),
};
let metadata =
StackSubstateMetadata::new(210_000, 1000, T::NewContractExtraBytes::get(), T::config());
let metadata = StackSubstateMetadata::new(210_000, 1000, T::config());
let state = SubstrateStackState::<T>::new(&vicinity, metadata);
let mut executor = StackExecutor::new(state, T::config());

Expand Down Expand Up @@ -832,8 +847,8 @@ pub mod module {
#[transactional]
pub fn selfdestruct(origin: OriginFor<T>, contract: EvmAddress) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let maintainer = T::AddressMapping::get_evm_address(&who).ok_or(Error::<T>::AddressNotMapped)?;
Self::do_selfdestruct(who, &maintainer, contract)?;
let caller = T::AddressMapping::get_evm_address(&who).ok_or(Error::<T>::AddressNotMapped)?;
Self::do_selfdestruct(&caller, &contract)?;

Pallet::<T>::deposit_event(Event::<T>::ContractSelfdestructed(contract));

Expand Down Expand Up @@ -867,16 +882,15 @@ impl<T: Config> Pallet<T> {
}

#[transactional]
pub fn remove_contract(address: &EvmAddress) -> Result<u32, DispatchError> {
let address_account = T::AddressMapping::get_account_id(address);
pub fn remove_contract(caller: &EvmAddress, contract: &EvmAddress) -> DispatchResult {
let contract_account = T::AddressMapping::get_account_id(contract);

let size = Accounts::<T>::try_mutate_exists(address, |account_info| -> Result<u32, DispatchError> {
Accounts::<T>::try_mutate_exists(contract, |account_info| -> DispatchResult {
// We will keep the nonce until the storages are cleared.
// Only remove the `contract_info`
let account_info = account_info.as_mut().ok_or(Error::<T>::ContractNotFound)?;
let contract_info = account_info.contract_info.take().ok_or(Error::<T>::ContractNotFound)?;

let maintainer_account = T::AddressMapping::get_account_id(&contract_info.maintainer);
T::TransferAll::transfer_all(&address_account, &maintainer_account)?;

CodeInfos::<T>::mutate_exists(&contract_info.code_hash, |maybe_code_info| {
if let Some(code_info) = maybe_code_info.as_mut() {
code_info.ref_count = code_info.ref_count.saturating_sub(1);
Expand All @@ -890,18 +904,23 @@ impl<T: Config> Pallet<T> {
}
});

AccountStorages::<T>::remove_prefix(address, None);

let size = ContractStorageSizes::<T>::take(address);
ContractStorageSizes::<T>::take(contract);

Ok(size)
T::IdleScheduler::schedule(
EvmTask::Remove {
caller: *caller,
contract: *contract,
maintainer: contract_info.maintainer,
}
.into(),
)
})?;

// this should happen after `Accounts` is updated because this could trigger another updates on
// `Accounts`
frame_system::Pallet::<T>::dec_providers(&address_account)?;
frame_system::Pallet::<T>::dec_providers(&contract_account)?;

Ok(size)
Ok(())
}

/// Removes an account from Accounts and AccountStorages.
Expand Down Expand Up @@ -969,11 +988,6 @@ impl<T: Config> Pallet<T> {
deployed: false,
};

Self::update_contract_storage_size(
&address,
code_size.saturating_add(T::NewContractExtraBytes::get()) as i32,
);

CodeInfos::<T>::mutate_exists(&code_hash, |maybe_code_info| {
if let Some(code_info) = maybe_code_info.as_mut() {
code_info.ref_count = code_info.ref_count.saturating_add(1);
Expand Down Expand Up @@ -1179,31 +1193,17 @@ impl<T: Config> Pallet<T> {
}

/// Selfdestruct a contract at a given address.
fn do_selfdestruct(who: T::AccountId, maintainer: &EvmAddress, contract: EvmAddress) -> DispatchResult {
fn do_selfdestruct(caller: &EvmAddress, contract: &EvmAddress) -> DispatchResult {
let account_info = Self::accounts(contract).ok_or(Error::<T>::ContractNotFound)?;
let contract_info = account_info
.contract_info
.as_ref()
.ok_or(Error::<T>::ContractNotFound)?;

ensure!(contract_info.maintainer == *maintainer, Error::<T>::NoPermission);
ensure!(contract_info.maintainer == *caller, Error::<T>::NoPermission);
ensure!(!contract_info.deployed, Error::<T>::ContractAlreadyDeployed);

let storage = Self::remove_contract(&contract)?;

let contract_account = T::AddressMapping::get_account_id(&contract);

let amount = T::StorageDepositPerByte::get().saturating_mul(storage.into());
let val = T::Currency::repatriate_reserved_named(
&RESERVE_ID_STORAGE_DEPOSIT,
&contract_account,
&who,
amount,
BalanceStatus::Free,
)?;
debug_assert!(val.is_zero());

Ok(())
Self::remove_contract(caller, contract)
}

fn ensure_root_or_signed(o: T::Origin) -> Result<Either<(), T::AccountId>, BadOrigin> {
Expand Down Expand Up @@ -1315,6 +1315,33 @@ impl<T: Config> Pallet<T> {

Ok(())
}

fn refund_storage(caller: &H160, contract: &H160, maintainer: &H160) -> DispatchResult {
let user = T::AddressMapping::get_account_id(caller);
let contract_acc = T::AddressMapping::get_account_id(contract);
let maintainer_acc = T::AddressMapping::get_account_id(maintainer);
let amount = T::Currency::reserved_balance_named(&RESERVE_ID_STORAGE_DEPOSIT, &contract_acc);

log::debug!(
target: "evm",
"refund_storage: [from: {:?}, account: {:?}, contract: {:?}, contract_acc: {:?}, maintainer: {:?}, maintainer_acc: {:?}, amount: {:?}]",
caller, user, contract, contract_acc, maintainer, maintainer_acc, amount
);

// user can't be a dead account
let val = T::Currency::repatriate_reserved_named(
&RESERVE_ID_STORAGE_DEPOSIT,
&contract_acc,
&user,
amount,
BalanceStatus::Free,
)?;
debug_assert!(val.is_zero());

T::TransferAll::transfer_all(&contract_acc, &maintainer_acc)?;

Ok(())
}
}

impl<T: Config> EVMTrait<T::AccountId> for Pallet<T> {
Expand Down Expand Up @@ -1494,3 +1521,94 @@ impl<T: Config + Send + Sync> SignedExtension for SetEvmOrigin<T> {
Ok(())
}
}

#[derive(Clone, RuntimeDebug, PartialEq, Encode, Decode, TypeInfo)]
pub enum EvmTask<T: Config> {
// TODO: update
Schedule {
from: EvmAddress,
target: EvmAddress,
input: Vec<u8>,
value: BalanceOf<T>,
gas_limit: u64,
storage_limit: u32,
},
Remove {
caller: EvmAddress,
contract: EvmAddress,
maintainer: EvmAddress,
},
}

impl<T: Config> DispatchableTask for EvmTask<T> {
fn dispatch(self, weight: Weight) -> TaskResult {
match self {
// TODO: update
EvmTask::Schedule { .. } => {
// check weight and call `scheduled_call`
TaskResult {
result: Ok(()),
used_weight: 0,
finished: false,
}
}
EvmTask::Remove {
caller,
contract,
maintainer,
} => {
// default limit 100
let limit = cmp::min(
weight
.checked_div(<T as frame_system::Config>::DbWeight::get().write)
.unwrap_or(100),
100,
) as u32;

match <AccountStorages<T>>::remove_prefix(contract, Some(limit)) {
AllRemoved(count) => {
let res = Pallet::<T>::refund_storage(&caller, &contract, &maintainer);
log::debug!(
target: "evm",
"EvmTask::Remove: [from: {:?}, contract: {:?}, maintainer: {:?}, count: {:?}, result: {:?}]",
caller, contract, maintainer, count, res
);

// Remove account after all of the storages are cleared.
Accounts::<T>::take(contract);

TaskResult {
result: res,
used_weight: <T as frame_system::Config>::DbWeight::get()
.write
.saturating_mul(count.into()),
finished: true,
}
}
SomeRemaining(count) => {
log::debug!(
target: "evm",
"EvmTask::Remove: [from: {:?}, contract: {:?}, maintainer: {:?}, count: {:?}]",
caller, contract, maintainer, count
);

TaskResult {
result: Ok(()),
used_weight: <T as frame_system::Config>::DbWeight::get()
.write
.saturating_mul(count.into()),
finished: false,
}
}
}
}
}
}
}

#[cfg(feature = "std")]
impl<T: Config> From<EvmTask<T>> for () {
fn from(_task: EvmTask<T>) -> Self {
unimplemented!()
}
}
Loading