mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
contracts: Multi block migrations (#14045)
* Frame Add translate_next This works similarly to to `translate` but only translate a single entry. This function will be useful in the context of multi-block migration. * Move to lazy migration * Updates * simplify MockMigration * wip * wip * add bench * add bench * fmt * fix bench * add . * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * Scalfold v10 / v11 fix tests * PR comment * tweak pub use * wip * wip * wip * misc merge master * misc merge master * wip * rm tmp stuff * wip * wip * wip * wip * wip * fixes * add state * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fix * fixed compilation * clean up logs * wip * Revert "Frame Add translate_next" This reverts commit 10318fc95c42b1f7f25efeb35e6d947ea02bed88. * Fix v10 logic * Apply suggestions from code review Co-authored-by: Alexander Theißen <alex.theissen@me.com> * wip * fixes * exercise del_queue * bump sample size * fmt * wip * blank line * fix lint * fix rustdoc job lint * PR comment do not use dangerous into() * Ad macros for updating mod visibility * Add doc * Add max_weight to integrity_test * fix compilation * Add no migration tests * ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts * fix clippy * PR review * Update frame/contracts/src/lib.rs Co-authored-by: Sasha Gryaznov <hi@agryaznov.com> * Fix master merge * fix merge 2 * fix tryruntime * fix lint --------- Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: command-bot <> Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
@@ -107,7 +107,10 @@ use crate::{
|
||||
use codec::{Codec, Decode, Encode, HasCompact};
|
||||
use environmental::*;
|
||||
use frame_support::{
|
||||
dispatch::{DispatchError, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, RawOrigin},
|
||||
dispatch::{
|
||||
DispatchError, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, RawOrigin,
|
||||
WithPostDispatchInfo,
|
||||
},
|
||||
ensure,
|
||||
error::BadOrigin,
|
||||
traits::{
|
||||
@@ -120,18 +123,18 @@ use frame_support::{
|
||||
use frame_system::{ensure_signed, pallet_prelude::OriginFor, EventRecord, Pallet as System};
|
||||
use pallet_contracts_primitives::{
|
||||
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
|
||||
ContractInstantiateResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue,
|
||||
StorageDeposit,
|
||||
ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult,
|
||||
InstantiateReturnValue, StorageDeposit,
|
||||
};
|
||||
use scale_info::TypeInfo;
|
||||
use smallvec::Array;
|
||||
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
|
||||
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, Zero};
|
||||
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
|
||||
|
||||
pub use crate::{
|
||||
address::{AddressGenerator, DefaultAddressGenerator},
|
||||
exec::Frame,
|
||||
migration::Migration,
|
||||
migration::{MigrateSequence, Migration, NoopMigration},
|
||||
pallet::*,
|
||||
schedule::{HostFnWeights, InstructionWeights, Limits, Schedule},
|
||||
wasm::Determinism,
|
||||
@@ -179,7 +182,12 @@ pub mod pallet {
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
/// The current storage version.
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(9);
|
||||
#[cfg(not(any(test, feature = "runtime-benchmarks")))]
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(11);
|
||||
|
||||
/// Hard coded storage version for running tests that depend on the current storage version.
|
||||
#[cfg(any(test, feature = "runtime-benchmarks"))]
|
||||
const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::storage_version(STORAGE_VERSION)]
|
||||
@@ -316,11 +324,22 @@ pub mod pallet {
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight {
|
||||
use migration::MigrateResult::*;
|
||||
|
||||
let (result, weight) = Migration::<T>::migrate(remaining_weight);
|
||||
let remaining_weight = remaining_weight.saturating_sub(weight);
|
||||
|
||||
if !matches!(result, Completed | NoMigrationInProgress) {
|
||||
return weight
|
||||
}
|
||||
|
||||
ContractInfo::<T>::process_deletion_queue_batch(remaining_weight)
|
||||
.saturating_add(T::WeightInfo::on_process_deletion_queue_batch())
|
||||
}
|
||||
|
||||
fn integrity_test() {
|
||||
Migration::<T>::integrity_test();
|
||||
|
||||
// Total runtime memory limit
|
||||
let max_runtime_mem: u32 = T::Schedule::get().limits.runtime_memory;
|
||||
// Memory limits for a single contract:
|
||||
@@ -499,6 +518,7 @@ pub mod pallet {
|
||||
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
||||
determinism: Determinism,
|
||||
) -> DispatchResult {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let origin = ensure_signed(origin)?;
|
||||
Self::bare_upload_code(origin, code, storage_deposit_limit.map(Into::into), determinism)
|
||||
.map(|_| ())
|
||||
@@ -514,6 +534,7 @@ pub mod pallet {
|
||||
origin: OriginFor<T>,
|
||||
code_hash: CodeHash<T>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let origin = ensure_signed(origin)?;
|
||||
<PrefabWasmModule<T>>::remove(&origin, code_hash)?;
|
||||
// we waive the fee because removing unused code is beneficial
|
||||
@@ -537,6 +558,7 @@ pub mod pallet {
|
||||
dest: AccountIdLookupOf<T>,
|
||||
code_hash: CodeHash<T>,
|
||||
) -> DispatchResult {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
ensure_root(origin)?;
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
<ContractInfoOf<T>>::try_mutate(&dest, |contract| {
|
||||
@@ -586,6 +608,7 @@ pub mod pallet {
|
||||
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
||||
data: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let common = CommonInput {
|
||||
origin: Origin::from_runtime_origin(origin)?,
|
||||
value,
|
||||
@@ -645,6 +668,7 @@ pub mod pallet {
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let code_len = code.len() as u32;
|
||||
let data_len = data.len() as u32;
|
||||
let salt_len = salt.len() as u32;
|
||||
@@ -687,6 +711,7 @@ pub mod pallet {
|
||||
data: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let data_len = data.len() as u32;
|
||||
let salt_len = salt.len() as u32;
|
||||
let common = CommonInput {
|
||||
@@ -709,6 +734,33 @@ pub mod pallet {
|
||||
T::WeightInfo::instantiate(data_len, salt_len),
|
||||
)
|
||||
}
|
||||
|
||||
/// When a migration is in progress, this dispatchable can be used to run migration steps.
|
||||
/// Calls that contribute to advancing the migration have their fees waived, as it's helpful
|
||||
/// for the chain. Note that while the migration is in progress, the pallet will also
|
||||
/// leverage the `on_idle` hooks to run migration steps.
|
||||
#[pallet::call_index(9)]
|
||||
#[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))]
|
||||
pub fn migrate(origin: OriginFor<T>, weight_limit: Weight) -> DispatchResultWithPostInfo {
|
||||
use migration::MigrateResult::*;
|
||||
ensure_signed(origin)?;
|
||||
|
||||
let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate());
|
||||
let (result, weight) = Migration::<T>::migrate(weight_limit);
|
||||
|
||||
match result {
|
||||
Completed =>
|
||||
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::No }),
|
||||
InProgress { steps_done, .. } if steps_done > 0 =>
|
||||
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::No }),
|
||||
InProgress { .. } =>
|
||||
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::Yes }),
|
||||
NoMigrationInProgress | NoMigrationPerformed => {
|
||||
let err: DispatchError = <Error<T>>::NoMigrationPerformed.into();
|
||||
Err(err.with_weight(T::WeightInfo::migrate()))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
@@ -861,6 +913,10 @@ pub mod pallet {
|
||||
CodeRejected,
|
||||
/// An indetermistic code was used in a context where this is not permitted.
|
||||
Indeterministic,
|
||||
/// A pending migration needs to complete before the extrinsic can be called.
|
||||
MigrationInProgress,
|
||||
/// Migrate dispatch call was attempted but no migration was performed.
|
||||
NoMigrationPerformed,
|
||||
}
|
||||
|
||||
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||
@@ -920,6 +976,10 @@ pub mod pallet {
|
||||
#[pallet::storage]
|
||||
pub(crate) type DeletionQueueCounter<T: Config> =
|
||||
StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
pub(crate) type MigrationInProgress<T: Config> =
|
||||
StorageValue<_, migration::Cursor, OptionQuery>;
|
||||
}
|
||||
|
||||
/// The type of origins supported by the contracts pallet.
|
||||
@@ -1210,6 +1270,21 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ensure_no_migration_in_progress {
|
||||
() => {
|
||||
if Migration::<T>::in_progress() {
|
||||
return ContractResult {
|
||||
gas_consumed: Zero::zero(),
|
||||
gas_required: Zero::zero(),
|
||||
storage_deposit: Default::default(),
|
||||
debug_message: Vec::new(),
|
||||
result: Err(Error::<T>::MigrationInProgress.into()),
|
||||
events: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Perform a call to a specified contract.
|
||||
///
|
||||
@@ -1234,6 +1309,8 @@ impl<T: Config> Pallet<T> {
|
||||
collect_events: CollectEvents,
|
||||
determinism: Determinism,
|
||||
) -> ContractExecResult<BalanceOf<T>, EventRecordOf<T>> {
|
||||
ensure_no_migration_in_progress!();
|
||||
|
||||
let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) {
|
||||
Some(DebugBufferVec::<T>::default())
|
||||
} else {
|
||||
@@ -1290,6 +1367,8 @@ impl<T: Config> Pallet<T> {
|
||||
debug: DebugInfo,
|
||||
collect_events: CollectEvents,
|
||||
) -> ContractInstantiateResult<T::AccountId, BalanceOf<T>, EventRecordOf<T>> {
|
||||
ensure_no_migration_in_progress!();
|
||||
|
||||
let mut debug_message = if debug == DebugInfo::UnsafeDebug {
|
||||
Some(DebugBufferVec::<T>::default())
|
||||
} else {
|
||||
@@ -1333,6 +1412,7 @@ impl<T: Config> Pallet<T> {
|
||||
storage_deposit_limit: Option<BalanceOf<T>>,
|
||||
determinism: Determinism,
|
||||
) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
|
||||
Migration::<T>::ensure_migrated()?;
|
||||
let schedule = T::Schedule::get();
|
||||
let module = PrefabWasmModule::from_code(
|
||||
code,
|
||||
@@ -1353,6 +1433,9 @@ impl<T: Config> Pallet<T> {
|
||||
|
||||
/// Query storage of a specified contract under a specified key.
|
||||
pub fn get_storage(address: T::AccountId, key: Vec<u8>) -> GetStorageResult {
|
||||
if Migration::<T>::in_progress() {
|
||||
return Err(ContractAccessError::MigrationInProgress)
|
||||
}
|
||||
let contract_info =
|
||||
ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user