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
218 changes: 14 additions & 204 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ members = [
"runtime/shibuya",
"tests/xcm-simulator",
"tests/integration",
"tests/utils",
"pallets/*",
"precompiles/*",
"primitives",
Expand Down Expand Up @@ -186,12 +185,9 @@ frame-metadata-hash-extension = { git = "https://github.com/paritytech/polkadot-
frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-aura = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-authorship = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-contracts = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-contracts-uapi = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-identity = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-multisig = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-migrations = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-insecure-randomness-collective-flip = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-session = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false, features = ["historical"] }
pallet-sudo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2512", default-features = false }
Expand Down Expand Up @@ -316,7 +312,6 @@ pallet-treasury = { path = "./vendor/treasury", default-features = false }
dapp-staking-runtime-api = { path = "./pallets/dapp-staking/rpc/runtime-api", default-features = false }

astar-primitives = { path = "./primitives", default-features = false }
astar-test-utils = { path = "./tests/utils", default-features = false }

pallet-evm-precompile-assets-erc20 = { path = "./precompiles/assets-erc20", default-features = false }
pallet-evm-precompile-sr25519 = { path = "./precompiles/sr25519", default-features = false }
Expand Down
6 changes: 2 additions & 4 deletions pallets/contracts-mbm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
pallet-balances = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
sp-io = { workspace = true }
Expand All @@ -27,6 +26,8 @@ sp-std = { workspace = true }
# `std` is required explicitly so `cargo test -p contracts-mbm` works standalone, without relying
# on workspace-wide feature unification.
pallet-migrations = { workspace = true, features = ["std"] }
# Surfaces the migrations' own logs under `RUST_LOG` when running the tests.
sp-tracing = { workspace = true, features = ["std"] }

[features]
default = ["std"]
Expand All @@ -40,19 +41,16 @@ std = [
"frame-support/std",
"frame-system/std",
"sp-runtime/std",
"pallet-balances/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"pallet-migrations/try-runtime",
"frame-system/try-runtime",
"sp-runtime/try-runtime",
"pallet-balances/try-runtime",
]
111 changes: 71 additions & 40 deletions pallets/contracts-mbm/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,100 @@

#![cfg(feature = "runtime-benchmarks")]

use crate::{Config, Pallet, ReleaseContractsDeposits};
use crate::{
purge::{clear_child_trie_metered, CONTRACT_INFO_OF},
Config, Pallet, PurgeContractsChildTries, RemovePalletStepped,
};
use frame_benchmarking::v2::*;
use frame_support::{
migrations::SteppedMigration,
traits::{
fungible::{InspectHold, Mutate, MutateHold},
Get,
},
storage::unhashed,
traits::Get,
weights::WeightMeter,
{StorageHasher, Twox64Concat},
};
use sp_runtime::traits::Zero;
use sp_std::marker::PhantomData;

/// Supplies the benchmark's hold reason as the `Get` bound the migration expects.
pub struct BenchHoldReason<T>(PhantomData<T>);
impl<T: Config> Get<<T as pallet_balances::Config>::RuntimeHoldReason> for BenchHoldReason<T> {
fn get() -> <T as pallet_balances::Config>::RuntimeHoldReason {
T::BenchmarkHoldReason::get()
}
}
use parity_scale_codec::Encode;
use sp_io::hashing::twox_128;
use sp_std::vec;

/// Upper bound of the value size component.
///
/// `pallet_contracts::Config::MaxCodeLen` was 123 KiB on every Astar runtime, and `PristineCode`
/// blobs are the largest values these migrations ever touch.
const MAX_VALUE_SIZE: u32 = 128 * 1024;

/// Destination of the swept deposit.
pub struct BenchEscrow<T>(PhantomData<T>);
impl<T: Config> Get<T::AccountId> for BenchEscrow<T> {
fn get() -> T::AccountId {
account("escrow", 0, 0)
/// Pallet the benchmarks operate on. Only the length of the name matters for the measurement.
pub struct BenchPallet;
impl Get<&'static str> for BenchPallet {
fn get() -> &'static str {
"Contracts"
}
}

type Weights<T> = crate::weights::SubstrateWeight<T>;

#[benchmarks]
mod benches {
use super::*;

/// Cost of settling one account.
/// Cost of seeking to, measuring and removing one top level key holding an `x` byte value.
///
/// Runs the real `step` against storage holding exactly one hold-bearing account, so the
/// `Holds` seek, the decode and the `transfer_on_hold` are all measured. The terminal
/// `next_key` probe that ends the loop is included too, which errs on the safe side.
/// Runs the real `step` against a prefix holding exactly one key, so the seek, the length
/// probe and the terminal `next_key` that ends the loop are all measured.
#[benchmark]
fn release_deposit() {
let contract: T::AccountId = account("contract", 0, 0);
let reason = T::BenchmarkHoldReason::get();
let amount: <T as pallet_balances::Config>::Balance = 1_000_000u32.into();
fn remove_key(x: Linear<0, MAX_VALUE_SIZE>) {
let prefix = twox_128(BenchPallet::get().as_bytes()).to_vec();
let key = [prefix.clone(), twox_128(b"PristineCode").to_vec()].concat();
unhashed::put_raw(&key, &vec![0u8; x as usize]);

pallet_balances::Pallet::<T>::set_balance(&contract, amount * 2u32.into());
pallet_balances::Pallet::<T>::hold(&reason, &contract, amount)
.expect("account was just funded");
let mut meter = WeightMeter::new();

#[block]
{
RemovePalletStepped::<BenchPallet, Weights<T>>::step(None, &mut meter)
.expect("migration step succeeds");
}

assert!(unhashed::get_raw(&key).is_none());
}

/// Cost of seeking to, measuring and removing one child trie key holding an `x` byte value.
#[benchmark]
fn remove_child_key(x: Linear<0, MAX_VALUE_SIZE>) {
let trie_id = vec![1u8; crate::TRIE_ID_LEN];
let key = twox_128(b"bench-key").to_vec();
sp_io::default_child_storage::set(&trie_id, &key, &vec![0u8; x as usize]);

let mut meter = WeightMeter::new();

#[block]
{
ReleaseContractsDeposits::<
T,
BenchHoldReason<T>,
BenchHoldReason<T>,
BenchEscrow<T>,
crate::weights::SubstrateWeight<T>,
>::step(None, &mut meter)
.expect("migration step succeeds");
clear_child_trie_metered::<Weights<T>>(&trie_id, &mut meter, 1);
}

assert!(sp_io::default_child_storage::next_key(&trie_id, &[]).is_none());
}

/// Cost of handing back the consumer reference `pallet-contracts` took on one contract
/// account, including recovering that account id from its `ContractInfoOf` key.
#[benchmark]
fn release_contract_consumer() {
let contract: T::AccountId = account("contract", 0, 0);
frame_system::Pallet::<T>::inc_providers(&contract);
frame_system::Pallet::<T>::inc_consumers(&contract).expect("account has a provider");

let key = [
PurgeContractsChildTries::<T, BenchPallet, Weights<T>>::map_prefix(CONTRACT_INFO_OF),
Twox64Concat::hash(&contract.encode()),
]
.concat();

#[block]
{
PurgeContractsChildTries::<T, BenchPallet, Weights<T>>::release_consumer(&key);
}

assert!(pallet_balances::Pallet::<T>::balance_on_hold(&reason, &contract).is_zero());
assert_eq!(frame_system::Pallet::<T>::consumers(&contract), 0);
}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime);
Expand Down
28 changes: 16 additions & 12 deletions pallets/contracts-mbm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@
//! Multi block migrations decommissioning `pallet-contracts` (ink!/Wasm smart contracts) from the
//! Astar, Shiden & Shibuya runtimes. Staged over two runtime upgrades:
//!
//! 1. [`ReleaseContractsDeposits`] - settles every balance the pallet still holds. Must complete
//! before the pallet index is dropped, otherwise those holds become undecodable and the funds
//! are frozen for good.
//! 2. *(next runtime)* purging the contract child tries and the pallet prefix.
//! 1. *(shipped)* `ReleaseContractsDeposits` - settled every balance the pallet still held. It had
//! to complete before the pallet index was dropped, otherwise those holds would have become
//! undecodable and the funds frozen for good. Removed here, along with the pallet: without the
//! `RuntimeHoldReason::Contracts` variant there is nothing left for it to match on.
//! 2. [`PurgeContractsChildTries`] then [`RemovePalletStepped`] - purge the contract child tries
//! and the leftover pallet prefixes, in that order.
//!
//! Both are size aware: every value is measured before it is deleted and charged through
//! benchmarked, size dependent weights, so a run of large values (`Contracts::PristineCode` blobs,
//! up to 123 KiB each) cannot produce a PoV oversized block.

#![cfg_attr(not(feature = "std"), no_std)]

Expand All @@ -35,8 +41,11 @@ mod mock;
#[cfg(test)]
mod tests;

mod release;
pub use release::ReleaseContractsDeposits;
mod purge;
pub use purge::{
MaxKeyLen, PurgeContractsChildTries, RemovePalletStepped, TrieId, CONTRACT_INFO_OF,
DELETION_QUEUE, MAX_KEYS_PER_STEP, TRIE_ID_LEN,
};

pub mod weights;
pub use weights::WeightInfo;
Expand All @@ -47,8 +56,6 @@ pub(crate) const LOG_TARGET: &str = "mbm::contracts";

#[frame_support::pallet]
pub mod pallet {
use frame_support::traits::Get;

/// Carries no storage and no calls. It exists only so that the migrations in this crate can
/// be benchmarked through the standard `benchmark pallet` tooling, and is therefore added to
/// the runtimes under `runtime-benchmarks` only.
Expand All @@ -57,8 +64,5 @@ pub mod pallet {
pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config + pallet_balances::Config {
/// Hold reason the `release_deposit` benchmark places on its sample account.
type BenchmarkHoldReason: Get<<Self as pallet_balances::Config>::RuntimeHoldReason>;
}
pub trait Config: frame_system::Config {}
}
81 changes: 22 additions & 59 deletions pallets/contracts-mbm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,101 +22,64 @@ use frame_support::{
construct_runtime, derive_impl,
migrations::MultiStepMigrator,
parameter_types,
traits::{ConstU64, OnFinalize, OnInitialize, VariantCount},
traits::{OnFinalize, OnInitialize},
weights::Weight,
};
use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::BuildStorage;

type Block = frame_system::mocking::MockBlock<Runtime>;

construct_runtime!(
pub struct Runtime {
System: frame_system,
Balances: pallet_balances,
MultiBlockMigrations: pallet_migrations,
ContractsMBM: crate,
}
);

impl crate::Config for Runtime {
type BenchmarkHoldReason = StorageDeposit;
}

/// Stands in for the runtime's `RuntimeHoldReason`. Written by hand rather than composed by
/// `construct_runtime!` so the mock does not have to pull in `pallet-contracts` - the migration is
/// generic over the reason, and the runtimes' real wiring is covered in `tests/integration`.
#[derive(
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Debug,
Encode,
Decode,
DecodeWithMemTracking,
MaxEncodedLen,
TypeInfo,
)]
pub enum HoldReason {
/// Stands in for `pallet_contracts::HoldReason::CodeUploadDepositReserve`.
CodeUploadDepositReserve,
/// Stands in for `pallet_contracts::HoldReason::StorageDepositReserve`.
StorageDepositReserve,
/// A hold owned by some other pallet, which must be left alone.
Unrelated,
}

impl VariantCount for HoldReason {
const VARIANT_COUNT: u32 = 3;
}
impl crate::Config for Runtime {}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Runtime {
type AccountData = pallet_balances::AccountData<u64>;
type Block = Block;
type MultiBlockMigrator = MultiBlockMigrations;
}

#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Runtime {
type AccountStore = System;
type RuntimeHoldReason = HoldReason;
type ExistentialDeposit = ConstU64<1>;
#[cfg(not(feature = "runtime-benchmarks"))]
mod migrations {
use super::{ContractsPalletName, Runtime};

pub type Weights = crate::weights::SubstrateWeight<Runtime>;
pub type Purge = crate::PurgeContractsChildTries<Runtime, ContractsPalletName, Weights>;
pub type Remove = crate::RemovePalletStepped<ContractsPalletName, Weights>;
}
#[cfg(not(feature = "runtime-benchmarks"))]
pub use migrations::{Purge, Remove};

#[derive_impl(pallet_migrations::config_preludes::TestDefaultConfig)]
impl pallet_migrations::Config for Runtime {
/// Mirrors the runtimes: the child tries must be purged before the prefix holding their
/// `trie_id`s is wiped.
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations = (
crate::ReleaseContractsDeposits<
Runtime,
CodeDeposit,
StorageDeposit,
EscrowAccount,
crate::weights::SubstrateWeight<Runtime>,
>,
);
type Migrations = (migrations::Purge, migrations::Remove);
#[cfg(feature = "runtime-benchmarks")]
type Migrations = pallet_migrations::mock_helpers::MockedMigrations;
type MigrationStatusHandler = ();
type MaxServiceWeight = MaxServiceWeight;
}

pub const ESCROW: u64 = 9_999;

parameter_types! {
pub const CodeDeposit: HoldReason = HoldReason::CodeUploadDepositReserve;
pub const StorageDeposit: HoldReason = HoldReason::StorageDepositReserve;
pub const EscrowAccount: u64 = ESCROW;
/// Mutable so a test can squeeze the budget and force the migration across several blocks.
pub static MaxServiceWeight: Weight = Weight::from_parts(2_000_000_000, 1_000_000);
/// Name of the pallet these migrations retire, matching the runtimes.
pub const ContractsPalletName: &'static str = "Contracts";
/// Mutable so a test can squeeze the budget and force a migration across several blocks.
pub static MaxServiceWeight: Weight = Weight::from_parts(50_000_000_000, 1_000_000);
}

pub fn new_test_ext() -> sp_io::TestExternalities {
// Makes the migrations' own logs visible with
// `RUST_LOG=mbm::contracts=debug cargo test -p contracts-mbm -- --nocapture`.
sp_tracing::try_init_simple();

let storage = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap();
Expand Down
Loading
Loading