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>,
}
impl<T: Config> Pallet<T>
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.
///
/// 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>>,
data: Vec<u8>,
debug: bool,
) -> 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(),
);
Alexander Theißen
committed
ContractExecResult {
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,
gas_limit: Weight,
storage_deposit_limit: Option<BalanceOf<T>>,
code: Code<CodeHash<T>>,
data: Vec<u8>,
salt: Vec<u8>,
debug: bool,
) -> ContractInstantiateResult<T::AccountId, BalanceOf<T>> {
let mut debug_message = if debug { Some(Vec::new()) } else { None };
let output = Self::internal_instantiate(
gas_limit,
code,
salt,
ContractInstantiateResult {
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(),
Alexander Theißen
committed
}
/// 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::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>,
) -> frame_support::dispatch::DispatchResult {
self::wasm::reinstrument(module, schedule).map(|_| ())
Jim Posen
committed
}
/// 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(
Alexander Theißen
committed
origin.clone(),
dest,
&mut gas_meter,
&schedule,
value,
data,
debug_message,
);
Alexander Theißen
committed
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,
gas_limit: Weight,
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(Bytes(binary)) => {
let executable = PrefabWasmModule::from_code(binary, &schedule, origin.clone())
.map_err(|(err, msg)| {
debug_message.as_mut().map(|buffer| buffer.extend(msg.as_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 mut storage_meter = StorageMeter::new(
&origin,
storage_deposit_limit,
value.saturating_add(extra_deposit),
)?;
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
Alexander Theißen
committed
origin.clone(),
executable,
&mut gas_meter,
&schedule,
data,
&salt,
debug_message,
);
storage_deposit = storage_meter
Alexander Theißen
committed
.into_deposit(&origin)
.saturating_add(&StorageDeposit::Charge(extra_deposit));
result
InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit }
Alexander Theißen
committed
/// 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(),
Alexander Theißen
committed
/// Return the existential deposit of [`Config::Currency`].
fn min_balance() -> BalanceOf<T> {
<T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
}