Repository: paritytech/substrate
File: frame/contracts/src/lib.rs (Rust)

Code before:
		/// # struct Runtime {};
		/// type Migrations = (v9::Migration<Runtime>, v10::Migration<Runtime>, v11::Migration<Runtime>);
		/// ```
		///
		/// If you have a single migration step, you can use a tuple with a single element:
		/// ```
		/// use pallet_contracts::migration::v9;
		/// # struct Runtime {};
		/// type Migrations = (v9::Migration<Runtime>,);
		/// ```
		type Migrations: MigrateSequence;
	}

	#[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 mut remaining_weight = remaining_weight;

			loop {
				let (result, weight) = Migration::<T>::migrate(remaining_weight);
				remaining_weight.saturating_reduce(weight);

				match result {
					// There is not enough weight to perform a migration, or make any progress, we
					// just return the remaining weight.
					NoMigrationPerformed | InProgress { steps_done: 0 } => return remaining_weight,
					// Migration is still in progress, we can start the next step.
					InProgress { .. } => continue,
					// Either no migration is in progress, or we are done with all migrations, we
					// can do some more other work with the remaining weight.
					Completed | NoMigrationInProgress => break,
				}
			}

			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:
			// Value stack size: 1Mb per contract, default defined in wasmi
			const MAX_STACK_SIZE: u32 = 1024 * 1024;
			// Heap limit is normally 16 mempages of 64kb each = 1Mb per contract
			let max_heap_size = T::Schedule::get().limits.max_memory_size();
			// Max call depth is CallStack::size() + 1
			let max_call_depth = u32::try_from(T::CallStack::size().saturating_add(1))

Review comment (bug):
I agree that having `u32::MAX` migration steps is currently unlikely to happen... let alone in a single block...
Still just as a thought, what would happen if we get an overflow here?

https://github.com/paritytech/substrate/blob/dbb20d3accc0331eb40af0c842427bfeeb430240/frame/contracts/src/migration.rs#L484

Probably in this `on_idle()` function , we'd just return as having not enough weight, while stil have it. Nothing critical (except that not all block weight has been spent), as at the next block the migration would continue.

But in the `migrate()` dispatchable: 

https://github.com/paritytech/substrate/blob/dbb20d3accc0331eb40af0c842427bfeeb430240/frame/contracts/src/lib.rs#L784-L785

this case could end up in undeservedly charging the account initiated it, with a possibly quite a substantial amount for the weight spent on the migration. Because migration did execute, and `steps_done` is `0` just because overflow happened.

So probably it's better to use safe math in `MigrateSequence::steps()`?

Code after:
		/// # struct Runtime {};
		/// type Migrations = (v9::Migration<Runtime>, v10::Migration<Runtime>, v11::Migration<Runtime>);
		/// ```
		///
		/// If you have a single migration step, you can use a tuple with a single element:
		/// ```
		/// use pallet_contracts::migration::v9;
		/// # struct Runtime {};
		/// type Migrations = (v9::Migration<Runtime>,);
		/// ```
		type Migrations: MigrateSequence;
	}

	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
		fn on_idle(_block: T::BlockNumber, mut remaining_weight: Weight) -> Weight {
			use migration::MigrateResult::*;

			loop {
				let (result, weight) = Migration::<T>::migrate(remaining_weight);
				remaining_weight.saturating_reduce(weight);

				match result {
					// There is not enough weight to perform a migration, or make any progress, we
					// just return the remaining weight.
					NoMigrationPerformed | InProgress { steps_done: 0 } => return remaining_weight,
					// Migration is still in progress, we can start the next step.
					InProgress { .. } => continue,
					// Either no migration is in progress, or we are done with all migrations, we
					// can do some more other work with the remaining weight.
					Completed | NoMigrationInProgress => break,
				}
			}

			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:
			// Value stack size: 1Mb per contract, default defined in wasmi
			const MAX_STACK_SIZE: u32 = 1024 * 1024;
			// Heap limit is normally 16 mempages of 64kb each = 1Mb per contract
			let max_heap_size = T::Schedule::get().limits.max_memory_size();
			// Max call depth is CallStack::size() + 1
			let max_call_depth = u32::try_from(T::CallStack::size().saturating_add(1))
				.expect("CallStack size is too big");