lib.rs 47.7 KiB
Newer Older
	///
	/// Child trie deletion is a heavy operation depending on the amount of storage items
	/// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.
	#[pallet::storage]
	pub(crate) type DeletionQueue<T: Config> =
		StorageValue<_, BoundedVec<DeletedContract, T::DeletionQueueDepth>, ValueQuery>;
/// Return type of the private [`Pallet::internal_call`] function.
type InternalCallOutput<T> = InternalOutput<T, ExecReturnValue>;

/// Return type of the private [`Pallet::internal_instantiate`] function.
type InternalInstantiateOutput<T> = InternalOutput<T, (AccountIdOf<T>, ExecReturnValue)>;

/// Return type of private helper functions.
struct InternalOutput<T: Config, O> {
	/// The gas meter that was used to execute the call.
	gas_meter: GasMeter<T>,
	/// The storage deposit used by the call.
	storage_deposit: StorageDeposit<BalanceOf<T>>,
	/// The result of the call.
	result: Result<O, ExecError>,
}

where
	T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
	/// Perform a call to a specified contract.
	///
	/// This function is similar to [`Self::call`], but doesn't perform any address lookups
	/// and better suitable for calling directly from Rust.
	/// # Note
	///
	/// `debug` should only ever be set to `true` when executing as an RPC because
	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
	/// If set to `true` it returns additional human readable debugging information.
	///
yjh's avatar
yjh committed
	/// It returns the execution result and the amount of used weight.
	pub fn bare_call(
		origin: T::AccountId,
		dest: T::AccountId,
		value: BalanceOf<T>,
		storage_deposit_limit: Option<BalanceOf<T>>,
	) -> ContractExecResult<BalanceOf<T>> {
		let mut debug_message = if debug { Some(Vec::new()) } else { None };
		let output = Self::internal_call(
			origin,
			dest,
			value,
			gas_limit,
			storage_deposit_limit,
			data,
			debug_message.as_mut(),
		);
			result: output.result.map_err(|r| r.error),
			gas_consumed: output.gas_meter.gas_consumed().ref_time(),
			gas_required: output.gas_meter.gas_required().ref_time(),
			storage_deposit: output.storage_deposit,
			debug_message: debug_message.unwrap_or_default(),
		}
	}

	/// Instantiate a new contract.
	///
	/// This function is similar to [`Self::instantiate`], but doesn't perform any address lookups
	/// and better suitable for calling directly from Rust.
	///
	/// It returns the execution result, account id and the amount of used weight.
	///
	/// # Note
	///
	/// `debug` should only ever be set to `true` when executing as an RPC because
	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
	/// If set to `true` it returns additional human readable debugging information.
	pub fn bare_instantiate(
		origin: T::AccountId,
		value: BalanceOf<T>,
		storage_deposit_limit: Option<BalanceOf<T>>,
		code: Code<CodeHash<T>>,
		data: Vec<u8>,
		salt: Vec<u8>,
	) -> ContractInstantiateResult<T::AccountId, BalanceOf<T>> {
		let mut debug_message = if debug { Some(Vec::new()) } else { None };
		let output = Self::internal_instantiate(
			storage_deposit_limit,
			debug_message.as_mut(),
			result: output
				.result
				.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
				.map_err(|e| e.error),
			gas_consumed: output.gas_meter.gas_consumed().ref_time(),
			gas_required: output.gas_meter.gas_required().ref_time(),
			storage_deposit: output.storage_deposit,
			debug_message: debug_message.unwrap_or_default(),
	/// Upload new code without instantiating a contract from it.
	///
	/// This function is similar to [`Self::upload_code`], but doesn't perform any address lookups
	/// and better suitable for calling directly from Rust.
	pub fn bare_upload_code(
		origin: T::AccountId,
		code: Vec<u8>,
		storage_deposit_limit: Option<BalanceOf<T>>,
	) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
		let schedule = T::Schedule::get();
		let module =
			PrefabWasmModule::from_code(code, &schedule, origin).map_err(|(err, _)| err)?;
		let deposit = module.open_deposit();
		if let Some(storage_deposit_limit) = storage_deposit_limit {
			ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
		}
		let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit };
		module.store()?;
		Ok(result)
	}

	/// Query storage of a specified contract under a specified key.
	pub fn get_storage(address: T::AccountId, key: Vec<u8>) -> GetStorageResult {
		let contract_info =
			ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
		let maybe_value = Storage::<T>::read(
			&contract_info.trie_id,
			&StorageKey::<T>::try_from(key).map_err(|_| ContractAccessError::KeyDecodingFailed)?,
		);
	/// Determine the address of a contract.
	/// This is the address generation function used by contract instantiation. See
	/// [`DefaultAddressGenerator`] for the default implementation.
	pub fn contract_address(
		deploying_address: &T::AccountId,
		code_hash: &CodeHash<T>,
		salt: &[u8],
	) -> T::AccountId {
		T::AddressGenerator::generate_address(deploying_address, code_hash, salt)
	/// Store code for benchmarks which does not check nor instrument the code.
	#[cfg(feature = "runtime-benchmarks")]
	fn store_code_raw(
		code: Vec<u8>,
		owner: T::AccountId,
	) -> frame_support::dispatch::DispatchResult {
		let schedule = T::Schedule::get();
		PrefabWasmModule::store_code_unchecked(code, &schedule, owner)?;
		Ok(())
	}

	/// This exists so that benchmarks can determine the weight of running an instrumentation.
	#[cfg(feature = "runtime-benchmarks")]
	fn reinstrument_module(
		module: &mut PrefabWasmModule<T>,
		schedule: &Schedule<T>,
	) -> frame_support::dispatch::DispatchResult {
		self::wasm::reinstrument(module, schedule).map(|_| ())

	/// Internal function that does the actual call.
	///
	/// Called by dispatchables and public functions.
	fn internal_call(
		origin: T::AccountId,
		dest: T::AccountId,
		value: BalanceOf<T>,
		gas_limit: Weight,
		storage_deposit_limit: Option<BalanceOf<T>>,
		data: Vec<u8>,
		debug_message: Option<&mut Vec<u8>>,
	) -> InternalCallOutput<T> {
		let mut gas_meter = GasMeter::new(gas_limit);
		let mut storage_meter = match StorageMeter::new(&origin, storage_deposit_limit, value) {
			Ok(meter) => meter,
			Err(err) =>
				return InternalCallOutput {
					result: Err(err.into()),
					gas_meter,
					storage_deposit: Default::default(),
				},
		};
		let schedule = T::Schedule::get();
		let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
			&mut storage_meter,
		InternalCallOutput {
			result,
			gas_meter,
			storage_deposit: storage_meter.into_deposit(&origin),
		}
	}

	/// Internal function that does the actual instantiation.
	///
	/// Called by dispatchables and public functions.
	fn internal_instantiate(
		origin: T::AccountId,
		value: BalanceOf<T>,
		storage_deposit_limit: Option<BalanceOf<T>>,
		code: Code<CodeHash<T>>,
		data: Vec<u8>,
		salt: Vec<u8>,
		mut debug_message: Option<&mut Vec<u8>>,
	) -> InternalInstantiateOutput<T> {
		let mut storage_deposit = Default::default();
		let mut gas_meter = GasMeter::new(gas_limit);
		let try_exec = || {
			let schedule = T::Schedule::get();
			let (extra_deposit, executable) = match code {
				Code::Upload(binary) => {
					let executable = PrefabWasmModule::from_code(binary, &schedule, origin.clone())
							debug_message.as_mut().map(|buffer| buffer.extend(msg.as_bytes()));
					// The open deposit will be charged during execution when the
					// uploaded module does not already exist. This deposit is not part of the
Sacha Lansky's avatar
Sacha Lansky committed
					// storage meter because it is not transferred to the contract but
					// reserved on the uploading account.
					(executable.open_deposit(), executable)
				Code::Existing(hash) => (
					Default::default(),
					PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter)?,
			let mut storage_meter = StorageMeter::new(
				&origin,
				storage_deposit_limit,
				value.saturating_add(extra_deposit),
			)?;
			let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
				&mut storage_meter,
			);
			storage_deposit = storage_meter
				.saturating_add(&StorageDeposit::Charge(extra_deposit));
			result
		InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit }
	/// Deposit a pallet contracts event. Handles the conversion to the overarching event type.
	fn deposit_event(topics: Vec<T::Hash>, event: Event<T>) {
		<frame_system::Pallet<T>>::deposit_event_indexed(
			&topics,
			<T as Config>::RuntimeEvent::from(event).into(),

	/// Return the existential deposit of [`Config::Currency`].
	fn min_balance() -> BalanceOf<T> {
		<T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
	}