Newer
Older
Sasha Gryaznov
committed
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 }),
}
}
Sasha Gryaznov
committed
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
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>;
Sasha Gryaznov
committed
}
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;
Sasha Gryaznov
committed
let mut storage_meter =
match StorageMeter::new(&origin, common.storage_deposit_limit, common.value) {
Sasha Gryaznov
committed
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()),
},
}
Sasha Gryaznov
committed
}
fn ensure_origin(&self, _origin: Origin<T>) -> Result<(), DispatchError> {
Ok(())
}
Sasha Gryaznov
committed
}
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()?;
Sasha Gryaznov
committed
let (extra_deposit, executable) = match &self.code {
Code::Upload(binary) => {
let executable = PrefabWasmModule::from_code(
binary.clone(),
&schedule,
Sasha Gryaznov
committed
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());
Sasha Gryaznov
committed
let mut storage_meter = StorageMeter::new(
Sasha Gryaznov
committed
common.storage_deposit_limit,
common.value.saturating_add(extra_deposit),
)?;
let CommonInput { value, data, debug_message, .. } = common;
Sasha Gryaznov
committed
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
origin.clone(),
executable,
&mut gas_meter,
&mut storage_meter,
&schedule,
value,
data.clone(),
&salt,
debug_message,
);
Sasha Gryaznov
committed
storage_deposit = storage_meter
.try_into_deposit(&contract_origin)?
Sasha Gryaznov
committed
.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),
}
}
Sasha Gryaznov
committed
}
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.
/// # 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,
determinism: Determinism,
) -> ContractExecResult<BalanceOf<T>> {
let mut debug_message = if debug { Some(DebugBufferVec::<T>::default()) } else { None };
let origin = Origin::from_account_id(origin);
Sasha Gryaznov
committed
let common = CommonInput {
Sasha Gryaznov
committed
data,
gas_limit,
storage_deposit_limit,
Sasha Gryaznov
committed
debug_message: debug_message.as_mut(),
};
let output = CallInput::<T> { dest, determinism }.run_guarded(common);
Alexander Theißen
committed
ContractExecResult {
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.
///
/// # 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(DebugBufferVec::<T>::default()) } else { None };
Sasha Gryaznov
committed
let common = CommonInput {
origin: Origin::from_account_id(origin),
Sasha Gryaznov
committed
data,
gas_limit,
Sasha Gryaznov
committed
debug_message: debug_message.as_mut(),
};
let output = InstantiateInput::<T> { code, salt }.run_guarded(common);
ContractInstantiateResult {
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(),
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>>,
determinism: Determinism,
) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
let schedule = T::Schedule::get();
Alexander Theißen
committed
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 {
let contract_info =
ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
Alexander Theißen
committed
let maybe_value = contract_info.read(
&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>,
input_data: &[u8],
salt: &[u8],
Alexander Theißen
committed
T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt)
/// Returns the code hash of the contract specified by `account` ID.
pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>> {
Alexander Theißen
committed
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>,
) -> frame_support::dispatch::DispatchResult {
self::wasm::reinstrument(module, schedule).map(|_| ())
Jim Posen
committed
}
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()
}
/// 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)
Jim Posen
committed
}
sp_api::decl_runtime_apis! {
/// The API used to dry-run contract interactions.
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
pub trait ContractsApi<AccountId, Balance, BlockNumber, Hash> where
AccountId: Codec,
Balance: Codec,
BlockNumber: Codec,
Hash: 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>;
/// 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>;
/// 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>,
determinism: Determinism,
) -> 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;
}
}