Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage Cleaner Precompile #1224

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4c9cdd0
add precompile clear-storage skeleton
librelois Oct 3, 2023
7a9cf23
implement precompile clear storage
librelois Oct 8, 2023
50c50fc
precompile storage cleaner
ahmadkaouk Oct 16, 2023
893ce1e
mock for testing
ahmadkaouk Oct 16, 2023
946ee9c
add some rust tests
ahmadkaouk Oct 16, 2023
8f80890
add more rust tests
ahmadkaouk Oct 16, 2023
aa01440
cover suicided contracts with no storage items
ahmadkaouk Oct 16, 2023
8e853c9
fix
ahmadkaouk Oct 16, 2023
c43ba7d
fix bugs
ahmadkaouk Oct 16, 2023
b93051c
fix precompile
ahmadkaouk Oct 19, 2023
997ec3c
Update precompile signature
ahmadkaouk Oct 19, 2023
a301ebb
fix precompile signature
ahmadkaouk Oct 19, 2023
76921f1
add a limit
ahmadkaouk Nov 2, 2023
d8e89ee
fix limit
ahmadkaouk Nov 15, 2023
3b5e877
Merge branch 'master' into ahmad-clear-suicided-storage
ahmadkaouk Jan 25, 2024
8f3deb4
Fix mock
ahmadkaouk Jan 25, 2024
a2d3a1f
update precompile
ahmadkaouk Jan 26, 2024
851bad5
format Cargo.toml
ahmadkaouk Jan 26, 2024
e2a5b3a
rust fmt
ahmadkaouk Jan 26, 2024
59f8436
fix clippy warnings
ahmadkaouk Jan 26, 2024
c848f79
fix format
ahmadkaouk Jan 26, 2024
4fa6d38
fix tests
ahmadkaouk Jan 26, 2024
5eab3c2
refactor precompile and record weights for clear_suicided_contract
ahmadkaouk Feb 28, 2024
0a40c2f
refactor tests
ahmadkaouk Feb 28, 2024
73d32bd
fix formatting
ahmadkaouk Feb 28, 2024
afc3a3a
Merge branch 'master' into ahmad-clear-suicided-storage
ahmadkaouk Feb 28, 2024
11d085d
refactor to record cost at the begining
ahmadkaouk Feb 29, 2024
c85332c
fix format
ahmadkaouk Mar 6, 2024
a2532bf
Fix clippy warnings
ahmadkaouk Mar 13, 2024
66c9805
Merge branch 'master' into ahmad-clear-suicided-storage
ahmadkaouk Mar 14, 2024
8e5fe4d
update Cargo.lock
ahmadkaouk Mar 14, 2024
0310225
Fix std feature
ahmadkaouk Apr 2, 2024
3e98d56
Merge branch 'master' into ahmad-clear-suicided-storage
ahmadkaouk Apr 2, 2024
dacc2bd
update dependencies
ahmadkaouk Apr 2, 2024
1c9f297
Fix clippy warnings
ahmadkaouk Apr 2, 2024
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
21 changes: 21 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"frame/evm/precompile/bls12377",
"frame/evm/precompile/dispatch",
"frame/evm/precompile/curve25519",
"frame/evm/precompile/clear-storage",
"client/api",
"client/consensus",
"client/rpc-core",
Expand Down
48 changes: 48 additions & 0 deletions frame/evm/precompile/clear-storage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[package]
name = "pallet-evm-precompile-clear-storage"
version = "2.0.0-dev"
license = "Apache-2.0"
description = "Storage cleanner precompiles for EVM pallet."
authors = { workspace = true }
edition = { workspace = true }
repository = { workspace = true }

[dependencies]
scale-codec = { package = "parity-scale-codec", workspace = true }
# Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
# Frontier
fp-evm = { workspace = true }
pallet-evm = { workspace = true }
precompile-utils = { workspace = true }

[dev-dependencies]
scale-info = { workspace = true }
# Substrate
frame-system = { workspace = true, features = ["default"] }
pallet-balances = { workspace = true, features = ["default", "insecure_zero_ed"] }
pallet-timestamp = { workspace = true, features = ["default"] }
pallet-utility = { workspace = true, features = ["default"] }
sp-core = { workspace = true, features = ["default"] }
sp-io = { workspace = true, features = ["default"] }
sp-runtime = { workspace = true, features = ["default"] }
sp-std = { workspace = true, features = ["default"] }
rlp = { workspace = true }

# Frontier
precompile-utils = { workspace = true, features = [ "std", "testing" ] }

[features]
default = ["std"]
std = [
"scale-codec/std",
# Substrate
"frame-support/std",
"sp-runtime/std",
# Frontier
"fp-evm/std",
"pallet-evm/std",
]
98 changes: 98 additions & 0 deletions frame/evm/precompile/clear-storage/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: Apache-2.0
// This file is part of Frontier.
//
// Copyright (c) 2020-2022 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)]
//#![deny(unused_crate_dependencies)]

extern crate alloc;

pub const ARRAY_LIMIT: u32 = 1_000;
pub const ENTRY_LIMIT: u32 = 1_000;

use core::marker::PhantomData;
use pallet_evm::AddressMapping;
use precompile_utils::{prelude::*, EvmResult};
use sp_runtime::traits::ConstU32;
#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

type GetArrayLimit = ConstU32<ARRAY_LIMIT>;

/// Storage cleaner precompile.
#[derive(Debug, Clone)]
pub struct StorageCleanerPrecompile<Runtime>(PhantomData<Runtime>);

#[precompile_utils::precompile]
impl<Runtime> StorageCleanerPrecompile<Runtime>
where
Runtime: pallet_evm::Config,
{
#[precompile::public("batchSome(address[])")]
ahmadkaouk marked this conversation as resolved.
Show resolved Hide resolved
fn clear_suicided_storage(
handle: &mut impl PrecompileHandle,
addresses: BoundedVec<Address, GetArrayLimit>,
) -> EvmResult {
let addresses: Vec<_> = addresses.into();
let mut deleted_entries = 0;

'inner: for address in addresses {
// Read Suicided storage item
// Suicided: Blake2128(16) + H160(20)
handle.record_db_read::<Runtime>(36)?;
if !pallet_evm::Pallet::<Runtime>::is_account_suicided(&address.0) {
return Err(revert(format!("NotSuicided: {}", address.0)));
}

ahmadkaouk marked this conversation as resolved.
Show resolved Hide resolved
let mut iter = pallet_evm::Pallet::<Runtime>::iter_account_storages(&address.0).drain();

// Delete a maximum of `ENTRY_LIMIT` entries in AccountStorages prefixed with `address`
while iter.next().is_some() {
handle.record_db_read::<Runtime>(116)?;
// Record the gas cost of deleting the storage item
handle.record_cost(RuntimeHelper::<Runtime>::db_write_gas_cost())?;

deleted_entries += 1;
if deleted_entries >= ENTRY_LIMIT {
if iter.next().is_none() {
handle.record_db_read::<Runtime>(116)?;
ahmadkaouk marked this conversation as resolved.
Show resolved Hide resolved
Self::clear_suicided_contract(address);
}
break 'inner;
}
}

// Record the cost of the iteration when `iter.next()` returned `None`
handle.record_db_read::<Runtime>(116)?;

// Remove the suicided account
Self::clear_suicided_contract(address);
}

Ok(())
}

fn clear_suicided_contract(address: Address) {
// Remove the address from the list of suicided contracts
pallet_evm::Suicided::<Runtime>::remove(&address.0);
// Decrement the sufficients of the account
let account_id = Runtime::AddressMapping::into_account_id(address.0);
let _ = frame_system::Pallet::<Runtime>::dec_sufficients(&account_id);
}
}
178 changes: 178 additions & 0 deletions frame/evm/precompile/clear-storage/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// SPDX-License-Identifier: Apache-2.0
// This file is part of Frontier.
//
// Copyright (c) 2020-2022 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.

//! Test mock for unit tests and benchmarking

use crate::{StorageCleanerPrecompile, StorageCleanerPrecompileCall};
use frame_support::{parameter_types, weights::Weight};
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, IdentityAddressMapping};
use precompile_utils::{testing::*, precompile_set::*};
use sp_core::{ConstU32, H256, U256};
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};

pub type AccountId = MockAccount;
pub type Balance = u128;

frame_support::construct_runtime! {
pub enum Runtime {
System: frame_system::{Pallet, Call, Storage, Config<T>, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage},
EVM: pallet_evm::{Pallet, Call, Storage, Config<T>, Event<T>},
}
}

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0));
}

impl frame_system::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = frame_system::mocking::MockBlock<Self>;
type BlockHashCount = BlockHashCount;
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}

parameter_types! {
pub const ExistentialDeposit: u64 = 0;
}

impl pallet_balances::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Balance = Balance;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type ReserveIdentifier = ();
type RuntimeHoldReason = ();
type FreezeIdentifier = ();
type MaxLocks = ();
type MaxReserves = ();
type MaxHolds = ();
type MaxFreezes = ();
}

parameter_types! {
pub const MinimumPeriod: u64 = 1000;
}
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = MinimumPeriod;
type WeightInfo = ();
}

pub type Precompiles<R> =
PrecompileSetBuilder<R, (PrecompileAt<AddressU64<1>, StorageCleanerPrecompile<R>>,)>;

pub type PCall = StorageCleanerPrecompileCall<Runtime>;

const BLOCK_GAS_LIMIT: u64 = 15_000_000;
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;

parameter_types! {
pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT);
pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE);
pub WeightPerGas: Weight = Weight::from_parts(20_000, 0);
pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
}

impl pallet_evm::Config for Runtime {
type FeeCalculator = ();
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
type CallOrigin = EnsureAddressRoot<Self::AccountId>;
type WithdrawOrigin = EnsureAddressNever<Self::AccountId>;
type AddressMapping = IdentityAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type PrecompilesType = Precompiles<Runtime>;
type PrecompilesValue = PrecompilesValue;
type ChainId = ();
type OnChargeTransaction = ();
type BlockGasLimit = BlockGasLimit;
type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
type FindAuthor = ();
type OnCreate = ();
type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
type Timestamp = Timestamp;
type WeightInfo = ();
}

/// Build test externalities, prepopulated with data for testing the precompile.
pub(crate) struct ExtBuilder {
balances: Vec<(AccountId, Balance)>,
}

impl Default for ExtBuilder {
fn default() -> Self {
Self {
balances: vec![],
}
}
}

impl ExtBuilder {
pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
self.balances = balances;
self
}

pub fn build(self) -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap();

pallet_balances::GenesisConfig::<Runtime> {
balances: self.balances,
}
.assimilate_storage(&mut t)
.unwrap();

let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
System::set_block_number(1);
});
ext
}
}
Loading