lib.rs 55.7 KiB
Newer Older
			Ok(RawOrigin::Signed(t)) => Ok(Self::Signed(t)),
			_ => Err(BadOrigin.into()),
		}
	}
	/// Returns the AccountId of a Signed Origin or an error if the origin is Root.
	pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> {
		match self {
			Origin::Signed(id) => Ok(id),
			Origin::Root => Err(DispatchError::RootNotAllowed),
		}
	}
}

/// Context of a contract invocation.
struct CommonInput<'a, T: Config> {
	value: BalanceOf<T>,
	data: Vec<u8>,
	gas_limit: Weight,
	storage_deposit_limit: Option<BalanceOf<T>>,
	debug_message: Option<&'a mut DebugBufferVec<T>>,
}
/// Input specific to a call into contract.
struct CallInput<T: Config> {
	dest: T::AccountId,
	determinism: Determinism,
}

/// Input specific to a contract instantiation invocation.
struct InstantiateInput<T: Config> {
	code: Code<CodeHash<T>>,
	salt: Vec<u8>,
}
/// Determines whether events should be collected during execution.
#[derive(PartialEq)]
pub enum CollectEvents {
	/// Collect events.
	///
	/// # Note
	///
	/// Events should only be collected when called off-chain, as this would otherwise
	/// collect all the Events emitted in the block so far and put them into the PoV.
	///
	/// **Never** use this mode for on-chain execution.
	UnsafeCollect,
	/// Skip event collection.
	Skip,
}

/// Determines whether debug messages will be collected.
#[derive(PartialEq)]
pub enum DebugInfo {
	/// Collect debug messages.
	/// # Note
	///
	/// This should only ever be set to `UnsafeDebug` when executing as an RPC because
	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
	UnsafeDebug,
	/// Skip collection of debug messages.
	Skip,
}

/// 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>,
}

/// Helper trait to wrap contract execution entry points into a single function
/// [`Invokable::run_guarded`].
trait Invokable<T: Config> {
	/// What is returned as a result of a successful invocation.
	type Output;

	/// Single entry point to contract execution.
	/// Downstream execution flow is branched by implementations of [`Invokable`] trait:
	///
	/// - [`InstantiateInput::run`] runs contract instantiation,
	/// - [`CallInput::run`] runs contract call.
	///
	/// We enforce a re-entrancy guard here by initializing and checking a boolean flag through a
	/// global reference.
	fn run_guarded(&self, common: CommonInput<T>) -> InternalOutput<T, Self::Output> {
		// Set up a global reference to the boolean flag used for the re-entrancy guard.
		environmental!(executing_contract: bool);

		let gas_limit = common.gas_limit;

		// Check whether the origin is allowed here. The logic of the access rules
		// is in the `ensure_origin`, this could vary for different implementations of this
		// trait. For example, some actions might not allow Root origin as they could require an
		// AccountId associated with the origin.
		if let Err(e) = self.ensure_origin(common.origin.clone()) {
			return InternalOutput {
				gas_meter: GasMeter::new(gas_limit),
				storage_deposit: Default::default(),
				result: Err(ExecError { error: e.into(), origin: ErrorOrigin::Caller }),
			}
		}

		executing_contract::using_once(&mut false, || {
			executing_contract::with(|f| {
				// Fail if already entered contract execution
				if *f {
					return Err(())
				}
				// We are entering contract execution
				*f = true;
				Ok(())
			})
			.expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
			.map_or_else(
				|_| InternalOutput {
					gas_meter: GasMeter::new(gas_limit),
					storage_deposit: Default::default(),
					result: Err(ExecError {
						error: <Error<T>>::ReentranceDenied.into(),
						origin: ErrorOrigin::Caller,
					}),
				},
				// Enter contract call.
				|_| self.run(common, GasMeter::new(gas_limit)),
			)
		})
	}

	/// Method that does the actual call to a contract. It can be either a call to a deployed
	/// contract or a instantiation of a new one.
	///
	/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
	fn run(
		&self,
		common: CommonInput<T>,
		gas_meter: GasMeter<T>,
	) -> InternalOutput<T, Self::Output>;

	/// This method ensures that the given `origin` is allowed to invoke the current `Invokable`.
	///
	/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
	fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError>;
}

impl<T: Config> Invokable<T> for CallInput<T> {
	type Output = ExecReturnValue;

	fn run(
		&self,
		common: CommonInput<T>,
		mut gas_meter: GasMeter<T>,
	) -> InternalOutput<T, Self::Output> {
		let CallInput { dest, determinism } = self;
		let CommonInput { origin, value, data, debug_message, .. } = common;
			match StorageMeter::new(&origin, common.storage_deposit_limit, common.value) {
				Ok(meter) => meter,
				Err(err) =>
					return InternalOutput {
						result: Err(err.into()),
						gas_meter,
						storage_deposit: Default::default(),
					},
			};
		let schedule = T::Schedule::get();
		let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
			origin.clone(),
			dest.clone(),
			&mut gas_meter,
			&mut storage_meter,
			&schedule,
			value,
			data.clone(),
			debug_message,
			*determinism,
		);

		match storage_meter.try_into_deposit(&origin) {
			Ok(storage_deposit) => InternalOutput { gas_meter, storage_deposit, result },
			Err(err) => InternalOutput {
				gas_meter,
				storage_deposit: Default::default(),
				result: Err(err.into()),
			},
		}

	fn ensure_origin(&self, _origin: Origin<T>) -> Result<(), DispatchError> {
		Ok(())
	}
}

impl<T: Config> Invokable<T> for InstantiateInput<T> {
	type Output = (AccountIdOf<T>, ExecReturnValue);

	fn run(
		&self,
		mut common: CommonInput<T>,
		mut gas_meter: GasMeter<T>,
	) -> InternalOutput<T, Self::Output> {
		let mut storage_deposit = Default::default();
		let try_exec = || {
			let schedule = T::Schedule::get();
			let InstantiateInput { salt, .. } = self;
			let CommonInput { origin: contract_origin, .. } = common;
			let origin = contract_origin.account_id()?;
			let (extra_deposit, executable) = match &self.code {
				Code::Upload(binary) => {
					let executable = PrefabWasmModule::from_code(
						binary.clone(),
						&schedule,
						Determinism::Enforced,
						TryInstantiate::Skip,
					)
					.map_err(|(err, msg)| {
						common
							.debug_message
							.as_mut()
							.map(|buffer| buffer.try_extend(&mut msg.bytes()));
						err
					})?;
					// The open deposit will be charged during execution when the
					// uploaded module does not already exist. This deposit is not part of the
					// 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 contract_origin = Origin::from_account_id(origin.clone());
			let mut storage_meter = StorageMeter::new(
				common.storage_deposit_limit,
				common.value.saturating_add(extra_deposit),
			)?;
			let CommonInput { value, data, debug_message, .. } = common;
			let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
				origin.clone(),
				executable,
				&mut gas_meter,
				&mut storage_meter,
				&schedule,
				value,
				data.clone(),
				&salt,
				debug_message,
			);
				.try_into_deposit(&contract_origin)?
				.saturating_add(&StorageDeposit::Charge(extra_deposit));
			result
		};
		InternalOutput { result: try_exec(), gas_meter, storage_deposit }
	}

	fn ensure_origin(&self, origin: Origin<T>) -> Result<(), DispatchError> {
		match origin {
			Origin::Signed(_) => Ok(()),
			Origin::Root => Err(DispatchError::RootNotAllowed),
		}
	}
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.
	///
	/// This function is similar to [`Self::call`], but doesn't perform any address lookups
	/// and better suitable for calling directly from Rust.
	/// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging
	/// information.
	/// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events
	/// emitted in the block so far and the ones emitted during the execution of this contract.
	pub fn bare_call(
		origin: T::AccountId,
		dest: T::AccountId,
		value: BalanceOf<T>,
		storage_deposit_limit: Option<BalanceOf<T>>,
		debug: DebugInfo,
		collect_events: CollectEvents,
	) -> ContractExecResult<BalanceOf<T>, EventRecordOf<T>> {
		ensure_no_migration_in_progress!();

		let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) {
			Some(DebugBufferVec::<T>::default())
		} else {
			None
		};
		let origin = Origin::from_account_id(origin);
			gas_limit,
			storage_deposit_limit,
			debug_message: debug_message.as_mut(),
		};
		let output = CallInput::<T> { dest, determinism }.run_guarded(common);
		let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
			Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
		} else {
			None
		};

			result: output.result.map_err(|r| r.error),
			gas_consumed: output.gas_meter.gas_consumed(),
			gas_required: output.gas_meter.gas_required(),
			storage_deposit: output.storage_deposit,
			debug_message: debug_message.unwrap_or_default().to_vec(),
		}
	}

	/// 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.
	///
	/// If `debug` is set to `DebugInfo::UnsafeDebug` it returns additional human readable debugging
	/// information.
	///
	/// If `collect_events` is set to `CollectEvents::UnsafeCollect` it collects all the Events
	/// emitted in the block so far.
	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>,
		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 {
			None
		};
			origin: Origin::from_account_id(origin),
			storage_deposit_limit,
			debug_message: debug_message.as_mut(),
		};
		let output = InstantiateInput::<T> { code, salt }.run_guarded(common);
		// collect events if CollectEvents is UnsafeCollect
		let events = if collect_events == CollectEvents::UnsafeCollect {
			Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
		} else {
			None
		};
			result: output
				.result
				.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
				.map_err(|e| e.error),
			gas_consumed: output.gas_meter.gas_consumed(),
			gas_required: output.gas_meter.gas_required(),
			storage_deposit: output.storage_deposit,
			debug_message: debug_message.unwrap_or_default().to_vec(),
	/// 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>> {
		Migration::<T>::ensure_migrated()?;
		let schedule = T::Schedule::get();
		let module = PrefabWasmModule::from_code(
			code,
			&schedule,
			origin,
			determinism,
			TryInstantiate::Instantiate,
		)
		.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 {
		if Migration::<T>::in_progress() {
			return Err(ContractAccessError::MigrationInProgress)
		}
		let contract_info =
			ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
			&Key::<T>::try_from_var(key)
				.map_err(|_| ContractAccessError::KeyDecodingFailed)?
				.into(),
	/// 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>,
	) -> T::AccountId {
		T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt)
Kevin Wang's avatar
Kevin Wang committed
	/// Returns the code hash of the contract specified by `account` ID.
	pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>> {
		ContractInfo::<T>::load_code_hash(account)
	/// 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(|_| ())
	/// 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()
	}
	/// Convert gas_limit from 1D Weight to a 2D Weight.
	/// Used by backwards compatible extrinsics. We cannot just set the proof_size weight limit to
	/// zero or an old `Call` will just fail with OutOfGas.
	fn compat_weight_limit(gas_limit: OldWeight) -> Weight {
		Weight::from_parts(gas_limit, u64::from(T::MaxCodeLen::get()) * 2)

sp_api::decl_runtime_apis! {
	/// The API used to dry-run contract interactions.
	#[api_version(2)]
	pub trait ContractsApi<AccountId, Balance, BlockNumber, Hash, EventRecord> where
		AccountId: Codec,
		Balance: Codec,
		BlockNumber: Codec,
		Hash: Codec,
		EventRecord: Codec,
	{
		/// Perform a call from a specified account to a given contract.
		///
		/// See [`crate::Pallet::bare_call`].
		fn call(
			origin: AccountId,
			dest: AccountId,
			value: Balance,
			gas_limit: Option<Weight>,
			storage_deposit_limit: Option<Balance>,
			input_data: Vec<u8>,
		) -> ContractExecResult<Balance, EventRecord>;

		/// Instantiate a new contract.
		///
		/// See `[crate::Pallet::bare_instantiate]`.
		fn instantiate(
			origin: AccountId,
			value: Balance,
			gas_limit: Option<Weight>,
			storage_deposit_limit: Option<Balance>,
			code: Code<Hash>,
			data: Vec<u8>,
			salt: Vec<u8>,
		) -> ContractInstantiateResult<AccountId, Balance, EventRecord>;

		/// Upload new code without instantiating a contract from it.
		///
		/// See [`crate::Pallet::bare_upload_code`].
		fn upload_code(
			origin: AccountId,
			code: Vec<u8>,
			storage_deposit_limit: Option<Balance>,
		) -> CodeUploadResult<Hash, Balance>;

		/// Query a given storage key in a given contract.
		///
		/// Returns `Ok(Some(Vec<u8>))` if the storage value exists under the given key in the
		/// specified account and `Ok(None)` if it doesn't. If the account specified by the address
		/// doesn't exist, or doesn't have a contract then `Err` is returned.
		fn get_storage(
			address: AccountId,
			key: Vec<u8>,
		) -> GetStorageResult;
	}
}