mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 03:27:58 +00:00
d8a7490146
* Expose environment types for offchain tooling * Use EnvironmentType wrapper * Add type impl to test config --------- Co-authored-by: parity-processbot <>
1719 lines
61 KiB
Rust
1719 lines
61 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! # Contracts Pallet
|
|
//!
|
|
//! The Contracts module provides functionality for the runtime to deploy and execute WebAssembly
|
|
//! smart-contracts.
|
|
//!
|
|
//! - [`Config`]
|
|
//! - [`Call`]
|
|
//!
|
|
//! ## Overview
|
|
//!
|
|
//! This module extends accounts based on the [`frame_support::traits::fungible`] traits to have
|
|
//! smart-contract functionality. It can be used with other modules that implement accounts based on
|
|
//! the [`frame_support::traits::fungible`] traits. These "smart-contract accounts" have the ability
|
|
//! to instantiate smart-contracts and make calls to other contract and non-contract accounts.
|
|
//!
|
|
//! The smart-contract code is stored once, and later retrievable via its hash.
|
|
//! This means that multiple smart-contracts can be instantiated from the same hash, without
|
|
//! replicating the code each time.
|
|
//!
|
|
//! When a smart-contract is called, its associated code is retrieved via the code hash and gets
|
|
//! executed. This call can alter the storage entries of the smart-contract account, instantiate new
|
|
//! smart-contracts, or call other smart-contracts.
|
|
//!
|
|
//! Finally, when an account is reaped, its associated code and storage of the smart-contract
|
|
//! account will also be deleted.
|
|
//!
|
|
//! ### Weight
|
|
//!
|
|
//! Senders must specify a [`Weight`] limit with every call, as all instructions invoked by the
|
|
//! smart-contract require weight. Unused weight is refunded after the call, regardless of the
|
|
//! execution outcome.
|
|
//!
|
|
//! If the weight limit is reached, then all calls and state changes (including balance transfers)
|
|
//! are only reverted at the current call's contract level. For example, if contract A calls B and B
|
|
//! runs out of gas mid-call, then all of B's calls are reverted. Assuming correct error handling by
|
|
//! contract A, A's other calls and state changes still persist.
|
|
//!
|
|
//! ### Notable Scenarios
|
|
//!
|
|
//! Contract call failures are not always cascading. When failures occur in a sub-call, they do not
|
|
//! "bubble up", and the call will only revert at the specific contract level. For example, if
|
|
//! contract A calls contract B, and B fails, A can decide how to handle that failure, either
|
|
//! proceeding or reverting A's changes.
|
|
//!
|
|
//! ## Interface
|
|
//!
|
|
//! ### Dispatchable functions
|
|
//!
|
|
//! * [`Pallet::instantiate_with_code`] - Deploys a new contract from the supplied Wasm binary,
|
|
//! optionally transferring
|
|
//! some balance. This instantiates a new smart contract account with the supplied code and
|
|
//! calls its constructor to initialize the contract.
|
|
//! * [`Pallet::instantiate`] - The same as `instantiate_with_code` but instead of uploading new
|
|
//! code an existing `code_hash` is supplied.
|
|
//! * [`Pallet::call`] - Makes a call to an account, optionally transferring some balance.
|
|
//! * [`Pallet::upload_code`] - Uploads new code without instantiating a contract from it.
|
|
//! * [`Pallet::remove_code`] - Removes the stored code and refunds the deposit to its owner. Only
|
|
//! allowed to code owner.
|
|
//! * [`Pallet::set_code`] - Changes the code of an existing contract. Only allowed to `Root`
|
|
//! origin.
|
|
//! * [`Pallet::migrate`] - Runs migration steps of current multi-block migration in priority,
|
|
//! before [`Hooks::on_idle`][frame_support::traits::Hooks::on_idle] activates.
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! * [`ink!`](https://use.ink) is language that enables writing Wasm-based smart contracts in plain
|
|
//! Rust.
|
|
|
|
#![allow(rustdoc::private_intra_doc_links)]
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")]
|
|
|
|
mod address;
|
|
mod benchmarking;
|
|
mod exec;
|
|
mod gas;
|
|
mod schedule;
|
|
mod storage;
|
|
mod wasm;
|
|
|
|
pub mod chain_extension;
|
|
pub mod migration;
|
|
pub mod unsafe_debug;
|
|
pub mod weights;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
use crate::{
|
|
exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, MomentOf, Stack as ExecStack},
|
|
gas::GasMeter,
|
|
storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
|
|
wasm::{CodeInfo, WasmBlob},
|
|
};
|
|
use codec::{Codec, Decode, Encode, HasCompact, MaxEncodedLen};
|
|
use environmental::*;
|
|
use frame_support::{
|
|
dispatch::{
|
|
DispatchError, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, RawOrigin,
|
|
WithPostDispatchInfo,
|
|
},
|
|
ensure,
|
|
error::BadOrigin,
|
|
traits::{
|
|
fungible::{Inspect, Mutate, MutateHold},
|
|
ConstU32, Contains, Get, Randomness, Time,
|
|
},
|
|
weights::Weight,
|
|
BoundedVec, DefaultNoBound, RuntimeDebug, RuntimeDebugNoBound,
|
|
};
|
|
use frame_system::{
|
|
ensure_signed,
|
|
pallet_prelude::{BlockNumberFor, OriginFor},
|
|
EventRecord, Pallet as System,
|
|
};
|
|
use pallet_contracts_primitives::{
|
|
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
|
|
ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult,
|
|
InstantiateReturnValue, StorageDeposit,
|
|
};
|
|
use scale_info::TypeInfo;
|
|
use smallvec::Array;
|
|
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, Zero};
|
|
use sp_std::{fmt::Debug, prelude::*};
|
|
|
|
pub use crate::{
|
|
address::{AddressGenerator, DefaultAddressGenerator},
|
|
exec::Frame,
|
|
migration::{MigrateSequence, Migration, NoopMigration},
|
|
pallet::*,
|
|
schedule::{HostFnWeights, InstructionWeights, Limits, Schedule},
|
|
wasm::Determinism,
|
|
};
|
|
pub use weights::WeightInfo;
|
|
|
|
#[cfg(doc)]
|
|
pub use crate::wasm::api_doc;
|
|
|
|
type CodeHash<T> = <T as frame_system::Config>::Hash;
|
|
type TrieId = BoundedVec<u8, ConstU32<128>>;
|
|
type BalanceOf<T> =
|
|
<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
|
|
type CodeVec<T> = BoundedVec<u8, <T as Config>::MaxCodeLen>;
|
|
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
|
|
type DebugBufferVec<T> = BoundedVec<u8, <T as Config>::MaxDebugBufferLen>;
|
|
type EventRecordOf<T> =
|
|
EventRecord<<T as frame_system::Config>::RuntimeEvent, <T as frame_system::Config>::Hash>;
|
|
|
|
/// The old weight type.
|
|
///
|
|
/// This is a copy of the [`frame_support::weights::OldWeight`] type since the contracts pallet
|
|
/// needs to support it indefinitely.
|
|
type OldWeight = u64;
|
|
|
|
/// Used as a sentinel value when reading and writing contract memory.
|
|
///
|
|
/// It is usually used to signal `None` to a contract when only a primitive is allowed
|
|
/// and we don't want to go through encoding a full Rust type. Using `u32::Max` is a safe
|
|
/// sentinel because contracts are never allowed to use such a large amount of resources
|
|
/// that this value makes sense for a memory location or length.
|
|
const SENTINEL: u32 = u32::MAX;
|
|
|
|
/// The target that is used for the log output emitted by this crate.
|
|
///
|
|
/// Hence you can use this target to selectively increase the log level for this crate.
|
|
///
|
|
/// Example: `RUST_LOG=runtime::contracts=debug my_code --dev`
|
|
const LOG_TARGET: &str = "runtime::contracts";
|
|
|
|
/// Wrapper around `PhantomData` to prevent it being filtered by `scale-info`.
|
|
///
|
|
/// `scale-info` filters out `PhantomData` fields because usually we are only interested
|
|
/// in sized types. However, when trying to communicate **types** as opposed to **values**
|
|
/// we want to have those zero sized types be included.
|
|
#[derive(Encode, Decode, DefaultNoBound, TypeInfo)]
|
|
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct EnvironmentType<T>(PhantomData<T>);
|
|
|
|
/// List of all runtime configurable types that are used in the communication between
|
|
/// `pallet-contracts` and any given contract.
|
|
///
|
|
/// Since those types are configurable they can vary between
|
|
/// chains all using `pallet-contracts`. Hence we need a mechanism to communicate those types
|
|
/// in a way that can be consumed by offchain tooling.
|
|
///
|
|
/// This type only exists in order to appear in the metadata where it can be read by
|
|
/// offchain tooling.
|
|
#[derive(Encode, Decode, DefaultNoBound, TypeInfo)]
|
|
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
|
|
#[scale_info(skip_type_params(T))]
|
|
pub struct Environment<T: Config> {
|
|
account_id: EnvironmentType<AccountIdOf<T>>,
|
|
balance: EnvironmentType<BalanceOf<T>>,
|
|
hash: EnvironmentType<<T as frame_system::Config>::Hash>,
|
|
hasher: EnvironmentType<<T as frame_system::Config>::Hashing>,
|
|
timestamp: EnvironmentType<MomentOf<T>>,
|
|
block_number: EnvironmentType<BlockNumberFor<T>>,
|
|
}
|
|
|
|
#[frame_support::pallet]
|
|
pub mod pallet {
|
|
use super::*;
|
|
use frame_support::pallet_prelude::*;
|
|
use frame_system::pallet_prelude::*;
|
|
use sp_runtime::Perbill;
|
|
|
|
/// The current storage version.
|
|
pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(15);
|
|
|
|
#[pallet::pallet]
|
|
#[pallet::storage_version(STORAGE_VERSION)]
|
|
pub struct Pallet<T>(_);
|
|
|
|
#[pallet::config]
|
|
pub trait Config: frame_system::Config {
|
|
/// The time implementation used to supply timestamps to contracts through `seal_now`.
|
|
type Time: Time;
|
|
|
|
/// The generator used to supply randomness to contracts through `seal_random`.
|
|
///
|
|
/// # Deprecated
|
|
///
|
|
/// Codes using the randomness functionality cannot be uploaded. Neither can contracts
|
|
/// be instantiated from existing codes that use this deprecated functionality. It will
|
|
/// be removed eventually. Hence for new `pallet-contracts` deployments it is okay
|
|
/// to supply a dummy implementation for this type (because it is never used).
|
|
type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>;
|
|
|
|
/// The fungible in which fees are paid and contract balances are held.
|
|
type Currency: Inspect<Self::AccountId>
|
|
+ Mutate<Self::AccountId>
|
|
+ MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
|
|
|
|
/// The overarching event type.
|
|
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
|
|
|
/// The overarching call type.
|
|
type RuntimeCall: Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
|
|
+ GetDispatchInfo
|
|
+ codec::Decode
|
|
+ IsType<<Self as frame_system::Config>::RuntimeCall>;
|
|
|
|
/// Filter that is applied to calls dispatched by contracts.
|
|
///
|
|
/// Use this filter to control which dispatchables are callable by contracts.
|
|
/// This is applied in **addition** to [`frame_system::Config::BaseCallFilter`].
|
|
/// It is recommended to treat this as a whitelist.
|
|
///
|
|
/// # Stability
|
|
///
|
|
/// The runtime **must** make sure that all dispatchables that are callable by
|
|
/// contracts remain stable. In addition [`Self::RuntimeCall`] itself must remain stable.
|
|
/// This means that no existing variants are allowed to switch their positions.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Note that dispatchables that are called via contracts do not spawn their
|
|
/// own wasm instance for each call (as opposed to when called via a transaction).
|
|
/// Therefore please make sure to be restrictive about which dispatchables are allowed
|
|
/// in order to not introduce a new DoS vector like memory allocation patterns that can
|
|
/// be exploited to drive the runtime into a panic.
|
|
type CallFilter: Contains<<Self as frame_system::Config>::RuntimeCall>;
|
|
|
|
/// Used to answer contracts' queries regarding the current weight price. This is **not**
|
|
/// used to calculate the actual fee and is only for informational purposes.
|
|
type WeightPrice: Convert<Weight, BalanceOf<Self>>;
|
|
|
|
/// Describes the weights of the dispatchables of this module and is also used to
|
|
/// construct a default cost schedule.
|
|
type WeightInfo: WeightInfo;
|
|
|
|
/// Type that allows the runtime authors to add new host functions for a contract to call.
|
|
type ChainExtension: chain_extension::ChainExtension<Self> + Default;
|
|
|
|
/// Cost schedule and limits.
|
|
#[pallet::constant]
|
|
type Schedule: Get<Schedule<Self>>;
|
|
|
|
/// The type of the call stack determines the maximum nesting depth of contract calls.
|
|
///
|
|
/// The allowed depth is `CallStack::size() + 1`.
|
|
/// Therefore a size of `0` means that a contract cannot use call or instantiate.
|
|
/// In other words only the origin called "root contract" is allowed to execute then.
|
|
///
|
|
/// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects
|
|
/// memory usage of your runtime.
|
|
type CallStack: Array<Item = Frame<Self>>;
|
|
|
|
/// The amount of balance a caller has to pay for each byte of storage.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Changing this value for an existing chain might need a storage migration.
|
|
#[pallet::constant]
|
|
type DepositPerByte: Get<BalanceOf<Self>>;
|
|
|
|
/// Fallback value to limit the storage deposit if it's not being set by the caller.
|
|
#[pallet::constant]
|
|
type DefaultDepositLimit: Get<BalanceOf<Self>>;
|
|
|
|
/// The amount of balance a caller has to pay for each storage item.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Changing this value for an existing chain might need a storage migration.
|
|
#[pallet::constant]
|
|
type DepositPerItem: Get<BalanceOf<Self>>;
|
|
|
|
/// The percentage of the storage deposit that should be held for using a code hash.
|
|
/// Instantiating a contract, or calling [`chain_extension::Ext::add_delegate_dependency`]
|
|
/// protects the code from being removed. In order to prevent abuse these actions are
|
|
/// protected with a percentage of the code deposit.
|
|
#[pallet::constant]
|
|
type CodeHashLockupDepositPercent: Get<Perbill>;
|
|
|
|
/// The address generator used to generate the addresses of contracts.
|
|
type AddressGenerator: AddressGenerator<Self>;
|
|
|
|
/// The maximum length of a contract code in bytes.
|
|
///
|
|
/// The value should be chosen carefully taking into the account the overall memory limit
|
|
/// your runtime has, as well as the [maximum allowed callstack
|
|
/// depth](#associatedtype.CallStack). Look into the `integrity_test()` for some insights.
|
|
#[pallet::constant]
|
|
type MaxCodeLen: Get<u32>;
|
|
|
|
/// The maximum allowable length in bytes for storage keys.
|
|
#[pallet::constant]
|
|
type MaxStorageKeyLen: Get<u32>;
|
|
|
|
/// The maximum number of delegate_dependencies that a contract can lock with
|
|
/// [`chain_extension::Ext::add_delegate_dependency`].
|
|
#[pallet::constant]
|
|
type MaxDelegateDependencies: Get<u32>;
|
|
|
|
/// Make contract callable functions marked as `#[unstable]` available.
|
|
///
|
|
/// Contracts that use `#[unstable]` functions won't be able to be uploaded unless
|
|
/// this is set to `true`. This is only meant for testnets and dev nodes in order to
|
|
/// experiment with new features.
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// Do **not** set to `true` on productions chains.
|
|
#[pallet::constant]
|
|
type UnsafeUnstableInterface: Get<bool>;
|
|
|
|
/// The maximum length of the debug buffer in bytes.
|
|
#[pallet::constant]
|
|
type MaxDebugBufferLen: Get<u32>;
|
|
|
|
/// Overarching hold reason.
|
|
type RuntimeHoldReason: From<HoldReason>;
|
|
|
|
/// The sequence of migration steps that will be applied during a migration.
|
|
///
|
|
/// # Examples
|
|
/// ```
|
|
/// use pallet_contracts::migration::{v10, v11};
|
|
/// # struct Runtime {};
|
|
/// # struct Currency {};
|
|
/// type Migrations = (v10::Migration<Runtime, Currency>, v11::Migration<Runtime>);
|
|
/// ```
|
|
///
|
|
/// If you have a single migration step, you can use a tuple with a single element:
|
|
/// ```
|
|
/// use pallet_contracts::migration::v10;
|
|
/// # struct Runtime {};
|
|
/// # struct Currency {};
|
|
/// type Migrations = (v10::Migration<Runtime, Currency>,);
|
|
/// ```
|
|
type Migrations: MigrateSequence;
|
|
|
|
/// Type that provides debug handling for the contract execution process.
|
|
///
|
|
/// # Warning
|
|
///
|
|
/// Do **not** use it in a production environment or for benchmarking purposes.
|
|
#[cfg(feature = "unsafe-debug")]
|
|
type Debug: unsafe_debug::UnsafeDebug<Self>;
|
|
|
|
/// Type that bundles together all the runtime configurable interface types.
|
|
///
|
|
/// This is not a real config. We just mention the type here as constant so that
|
|
/// its type appears in the metadata. Only valid value is `()`.
|
|
#[pallet::constant]
|
|
type Environment: Get<Environment<Self>>;
|
|
}
|
|
|
|
#[pallet::hooks]
|
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
|
fn on_idle(_block: BlockNumberFor<T>, 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");
|
|
|
|
// Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken.
|
|
//
|
|
// In worst case, the decoded Wasm contract code would be `x16` times larger than the
|
|
// encoded one. This is because even a single-byte wasm instruction has 16-byte size in
|
|
// wasmi. This gives us `MaxCodeLen*16` safety margin.
|
|
//
|
|
// Next, the pallet keeps the Wasm blob for each
|
|
// contract, hence we add up `MaxCodeLen` to the safety margin.
|
|
//
|
|
// Finally, the inefficiencies of the freeing-bump allocator
|
|
// being used in the client for the runtime memory allocations, could lead to possible
|
|
// memory allocations for contract code grow up to `x4` times in some extreme cases,
|
|
// which gives us total multiplier of `17*4` for `MaxCodeLen`.
|
|
//
|
|
// That being said, for every contract executed in runtime, at least `MaxCodeLen*17*4`
|
|
// memory should be available. Note that maximum allowed heap memory and stack size per
|
|
// each contract (stack frame) should also be counted.
|
|
//
|
|
// Finally, we allow 50% of the runtime memory to be utilized by the contracts call
|
|
// stack, keeping the rest for other facilities, such as PoV, etc.
|
|
//
|
|
// This gives us the following formula:
|
|
//
|
|
// `(MaxCodeLen * 17 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth <
|
|
// max_runtime_mem/2`
|
|
//
|
|
// Hence the upper limit for the `MaxCodeLen` can be defined as follows:
|
|
let code_len_limit = max_runtime_mem
|
|
.saturating_div(2)
|
|
.saturating_div(max_call_depth)
|
|
.saturating_sub(max_heap_size)
|
|
.saturating_sub(MAX_STACK_SIZE)
|
|
.saturating_div(17 * 4);
|
|
|
|
assert!(
|
|
T::MaxCodeLen::get() < code_len_limit,
|
|
"Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \
|
|
(current value is {:?}), to avoid possible runtime oom issues.",
|
|
max_call_depth,
|
|
code_len_limit,
|
|
T::MaxCodeLen::get(),
|
|
);
|
|
|
|
// Debug buffer should at least be large enough to accommodate a simple error message
|
|
const MIN_DEBUG_BUF_SIZE: u32 = 256;
|
|
assert!(
|
|
T::MaxDebugBufferLen::get() > MIN_DEBUG_BUF_SIZE,
|
|
"Debug buffer should have minimum size of {} (current setting is {})",
|
|
MIN_DEBUG_BUF_SIZE,
|
|
T::MaxDebugBufferLen::get(),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[pallet::call]
|
|
impl<T: Config> Pallet<T>
|
|
where
|
|
<BalanceOf<T> as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode,
|
|
{
|
|
/// Deprecated version if [`Self::call`] for use in an in-storage `Call`.
|
|
#[pallet::call_index(0)]
|
|
#[pallet::weight(T::WeightInfo::call().saturating_add(<Pallet<T>>::compat_weight_limit(*gas_limit)))]
|
|
#[allow(deprecated)]
|
|
#[deprecated(note = "1D weight is used in this extrinsic, please migrate to `call`")]
|
|
pub fn call_old_weight(
|
|
origin: OriginFor<T>,
|
|
dest: AccountIdLookupOf<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
#[pallet::compact] gas_limit: OldWeight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
data: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Self::call(
|
|
origin,
|
|
dest,
|
|
value,
|
|
<Pallet<T>>::compat_weight_limit(gas_limit),
|
|
storage_deposit_limit,
|
|
data,
|
|
)
|
|
}
|
|
|
|
/// Deprecated version if [`Self::instantiate_with_code`] for use in an in-storage `Call`.
|
|
#[pallet::call_index(1)]
|
|
#[pallet::weight(
|
|
T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32)
|
|
.saturating_add(<Pallet<T>>::compat_weight_limit(*gas_limit))
|
|
)]
|
|
#[allow(deprecated)]
|
|
#[deprecated(
|
|
note = "1D weight is used in this extrinsic, please migrate to `instantiate_with_code`"
|
|
)]
|
|
pub fn instantiate_with_code_old_weight(
|
|
origin: OriginFor<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
#[pallet::compact] gas_limit: OldWeight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
code: Vec<u8>,
|
|
data: Vec<u8>,
|
|
salt: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Self::instantiate_with_code(
|
|
origin,
|
|
value,
|
|
<Pallet<T>>::compat_weight_limit(gas_limit),
|
|
storage_deposit_limit,
|
|
code,
|
|
data,
|
|
salt,
|
|
)
|
|
}
|
|
|
|
/// Deprecated version if [`Self::instantiate`] for use in an in-storage `Call`.
|
|
#[pallet::call_index(2)]
|
|
#[pallet::weight(
|
|
T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(<Pallet<T>>::compat_weight_limit(*gas_limit))
|
|
)]
|
|
#[allow(deprecated)]
|
|
#[deprecated(note = "1D weight is used in this extrinsic, please migrate to `instantiate`")]
|
|
pub fn instantiate_old_weight(
|
|
origin: OriginFor<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
#[pallet::compact] gas_limit: OldWeight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
code_hash: CodeHash<T>,
|
|
data: Vec<u8>,
|
|
salt: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Self::instantiate(
|
|
origin,
|
|
value,
|
|
<Pallet<T>>::compat_weight_limit(gas_limit),
|
|
storage_deposit_limit,
|
|
code_hash,
|
|
data,
|
|
salt,
|
|
)
|
|
}
|
|
|
|
/// Upload new `code` without instantiating a contract from it.
|
|
///
|
|
/// If the code does not already exist a deposit is reserved from the caller
|
|
/// and unreserved only when [`Self::remove_code`] is called. The size of the reserve
|
|
/// depends on the size of the supplied `code`.
|
|
///
|
|
/// If the code already exists in storage it will still return `Ok` and upgrades
|
|
/// the in storage version to the current
|
|
/// [`InstructionWeights::version`](InstructionWeights).
|
|
///
|
|
/// - `determinism`: If this is set to any other value but [`Determinism::Enforced`] then
|
|
/// the only way to use this code is to delegate call into it from an offchain execution.
|
|
/// Set to [`Determinism::Enforced`] if in doubt.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Anyone can instantiate a contract from any uploaded code and thus prevent its removal.
|
|
/// To avoid this situation a constructor could employ access control so that it can
|
|
/// only be instantiated by permissioned entities. The same is true when uploading
|
|
/// through [`Self::instantiate_with_code`].
|
|
#[pallet::call_index(3)]
|
|
#[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
|
|
pub fn upload_code(
|
|
origin: OriginFor<T>,
|
|
code: Vec<u8>,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
determinism: Determinism,
|
|
) -> DispatchResult {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let origin = ensure_signed(origin)?;
|
|
Self::bare_upload_code(origin, code, storage_deposit_limit.map(Into::into), determinism)
|
|
.map(|_| ())
|
|
}
|
|
|
|
/// Remove the code stored under `code_hash` and refund the deposit to its owner.
|
|
///
|
|
/// A code can only be removed by its original uploader (its owner) and only if it is
|
|
/// not used by any contract.
|
|
#[pallet::call_index(4)]
|
|
#[pallet::weight(T::WeightInfo::remove_code())]
|
|
pub fn remove_code(
|
|
origin: OriginFor<T>,
|
|
code_hash: CodeHash<T>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let origin = ensure_signed(origin)?;
|
|
<WasmBlob<T>>::remove(&origin, code_hash)?;
|
|
// we waive the fee because removing unused code is beneficial
|
|
Ok(Pays::No.into())
|
|
}
|
|
|
|
/// Privileged function that changes the code of an existing contract.
|
|
///
|
|
/// This takes care of updating refcounts and all other necessary operations. Returns
|
|
/// an error if either the `code_hash` or `dest` do not exist.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// This does **not** change the address of the contract in question. This means
|
|
/// that the contract address is no longer derived from its code hash after calling
|
|
/// this dispatchable.
|
|
#[pallet::call_index(5)]
|
|
#[pallet::weight(T::WeightInfo::set_code())]
|
|
pub fn set_code(
|
|
origin: OriginFor<T>,
|
|
dest: AccountIdLookupOf<T>,
|
|
code_hash: CodeHash<T>,
|
|
) -> DispatchResult {
|
|
Migration::<T>::ensure_migrated()?;
|
|
ensure_root(origin)?;
|
|
let dest = T::Lookup::lookup(dest)?;
|
|
<ContractInfoOf<T>>::try_mutate(&dest, |contract| {
|
|
let contract = if let Some(contract) = contract {
|
|
contract
|
|
} else {
|
|
return Err(<Error<T>>::ContractNotFound.into())
|
|
};
|
|
<WasmBlob<T>>::increment_refcount(code_hash)?;
|
|
<WasmBlob<T>>::decrement_refcount(contract.code_hash);
|
|
Self::deposit_event(
|
|
vec![T::Hashing::hash_of(&dest), code_hash, contract.code_hash],
|
|
Event::ContractCodeUpdated {
|
|
contract: dest.clone(),
|
|
new_code_hash: code_hash,
|
|
old_code_hash: contract.code_hash,
|
|
},
|
|
);
|
|
contract.code_hash = code_hash;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
/// Makes a call to an account, optionally transferring some balance.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `dest`: Address of the contract to call.
|
|
/// * `value`: The balance to transfer from the `origin` to `dest`.
|
|
/// * `gas_limit`: The gas limit enforced when executing the constructor.
|
|
/// * `storage_deposit_limit`: The maximum amount of balance that can be charged from the
|
|
/// caller to pay for the storage consumed.
|
|
/// * `data`: The input data to pass to the contract.
|
|
///
|
|
/// * If the account is a smart-contract account, the associated code will be
|
|
/// executed and any value will be transferred.
|
|
/// * If the account is a regular account, any value will be transferred.
|
|
/// * If no account exists and the call value is not less than `existential_deposit`,
|
|
/// a regular account will be created and any value will be transferred.
|
|
#[pallet::call_index(6)]
|
|
#[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
|
|
pub fn call(
|
|
origin: OriginFor<T>,
|
|
dest: AccountIdLookupOf<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
gas_limit: Weight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
data: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let common = CommonInput {
|
|
origin: Origin::from_runtime_origin(origin)?,
|
|
value,
|
|
data,
|
|
gas_limit: gas_limit.into(),
|
|
storage_deposit_limit: storage_deposit_limit.map(Into::into),
|
|
debug_message: None,
|
|
};
|
|
let dest = T::Lookup::lookup(dest)?;
|
|
let mut output =
|
|
CallInput::<T> { dest, determinism: Determinism::Enforced }.run_guarded(common);
|
|
if let Ok(retval) = &output.result {
|
|
if retval.did_revert() {
|
|
output.result = Err(<Error<T>>::ContractReverted.into());
|
|
}
|
|
}
|
|
output.gas_meter.into_dispatch_result(output.result, T::WeightInfo::call())
|
|
}
|
|
|
|
/// Instantiates a new contract from the supplied `code` optionally transferring
|
|
/// some balance.
|
|
///
|
|
/// This dispatchable has the same effect as calling [`Self::upload_code`] +
|
|
/// [`Self::instantiate`]. Bundling them together provides efficiency gains. Please
|
|
/// also check the documentation of [`Self::upload_code`].
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// * `value`: The balance to transfer from the `origin` to the newly created contract.
|
|
/// * `gas_limit`: The gas limit enforced when executing the constructor.
|
|
/// * `storage_deposit_limit`: The maximum amount of balance that can be charged/reserved
|
|
/// from the caller to pay for the storage consumed.
|
|
/// * `code`: The contract code to deploy in raw bytes.
|
|
/// * `data`: The input data to pass to the contract constructor.
|
|
/// * `salt`: Used for the address derivation. See [`Pallet::contract_address`].
|
|
///
|
|
/// Instantiation is executed as follows:
|
|
///
|
|
/// - The supplied `code` is deployed, and a `code_hash` is created for that code.
|
|
/// - If the `code_hash` already exists on the chain the underlying `code` will be shared.
|
|
/// - The destination address is computed based on the sender, code_hash and the salt.
|
|
/// - The smart-contract account is created at the computed address.
|
|
/// - The `value` is transferred to the new account.
|
|
/// - The `deploy` function is executed in the context of the newly-created account.
|
|
#[pallet::call_index(7)]
|
|
#[pallet::weight(
|
|
T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32)
|
|
.saturating_add(*gas_limit)
|
|
)]
|
|
pub fn instantiate_with_code(
|
|
origin: OriginFor<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
gas_limit: Weight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
code: Vec<u8>,
|
|
data: Vec<u8>,
|
|
salt: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let origin = ensure_signed(origin)?;
|
|
let code_len = code.len() as u32;
|
|
|
|
let (module, upload_deposit) = Self::try_upload_code(
|
|
origin.clone(),
|
|
code,
|
|
storage_deposit_limit.clone().map(Into::into),
|
|
Determinism::Enforced,
|
|
None,
|
|
)?;
|
|
|
|
// Reduces the storage deposit limit by the amount that was reserved for the upload.
|
|
let storage_deposit_limit =
|
|
storage_deposit_limit.map(|limit| limit.into().saturating_sub(upload_deposit));
|
|
|
|
let data_len = data.len() as u32;
|
|
let salt_len = salt.len() as u32;
|
|
let common = CommonInput {
|
|
origin: Origin::from_account_id(origin),
|
|
value,
|
|
data,
|
|
gas_limit,
|
|
storage_deposit_limit,
|
|
debug_message: None,
|
|
};
|
|
|
|
let mut output =
|
|
InstantiateInput::<T> { code: WasmCode::Wasm(module), salt }.run_guarded(common);
|
|
if let Ok(retval) = &output.result {
|
|
if retval.1.did_revert() {
|
|
output.result = Err(<Error<T>>::ContractReverted.into());
|
|
}
|
|
}
|
|
|
|
output.gas_meter.into_dispatch_result(
|
|
output.result.map(|(_address, output)| output),
|
|
T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len),
|
|
)
|
|
}
|
|
|
|
/// Instantiates a contract from a previously deployed wasm binary.
|
|
///
|
|
/// This function is identical to [`Self::instantiate_with_code`] but without the
|
|
/// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary
|
|
/// must be supplied.
|
|
#[pallet::call_index(8)]
|
|
#[pallet::weight(
|
|
T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(*gas_limit)
|
|
)]
|
|
pub fn instantiate(
|
|
origin: OriginFor<T>,
|
|
#[pallet::compact] value: BalanceOf<T>,
|
|
gas_limit: Weight,
|
|
storage_deposit_limit: Option<<BalanceOf<T> as codec::HasCompact>::Type>,
|
|
code_hash: CodeHash<T>,
|
|
data: Vec<u8>,
|
|
salt: Vec<u8>,
|
|
) -> DispatchResultWithPostInfo {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let data_len = data.len() as u32;
|
|
let salt_len = salt.len() as u32;
|
|
let common = CommonInput {
|
|
origin: Origin::from_runtime_origin(origin)?,
|
|
value,
|
|
data,
|
|
gas_limit,
|
|
storage_deposit_limit: storage_deposit_limit.map(Into::into),
|
|
debug_message: None,
|
|
};
|
|
let mut output = InstantiateInput::<T> { code: WasmCode::CodeHash(code_hash), salt }
|
|
.run_guarded(common);
|
|
if let Ok(retval) = &output.result {
|
|
if retval.1.did_revert() {
|
|
output.result = Err(<Error<T>>::ContractReverted.into());
|
|
}
|
|
}
|
|
output.gas_meter.into_dispatch_result(
|
|
output.result.map(|(_address, output)| output),
|
|
T::WeightInfo::instantiate(data_len, salt_len),
|
|
)
|
|
}
|
|
|
|
/// When a migration is in progress, this dispatchable can be used to run migration steps.
|
|
/// Calls that contribute to advancing the migration have their fees waived, as it's helpful
|
|
/// for the chain. Note that while the migration is in progress, the pallet will also
|
|
/// leverage the `on_idle` hooks to run migration steps.
|
|
#[pallet::call_index(9)]
|
|
#[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))]
|
|
pub fn migrate(origin: OriginFor<T>, weight_limit: Weight) -> DispatchResultWithPostInfo {
|
|
use migration::MigrateResult::*;
|
|
ensure_signed(origin)?;
|
|
|
|
let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate());
|
|
let (result, weight) = Migration::<T>::migrate(weight_limit);
|
|
|
|
match result {
|
|
Completed =>
|
|
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::No }),
|
|
InProgress { steps_done, .. } if steps_done > 0 =>
|
|
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::No }),
|
|
InProgress { .. } =>
|
|
Ok(PostDispatchInfo { actual_weight: Some(weight), pays_fee: Pays::Yes }),
|
|
NoMigrationInProgress | NoMigrationPerformed => {
|
|
let err: DispatchError = <Error<T>>::NoMigrationPerformed.into();
|
|
Err(err.with_weight(T::WeightInfo::migrate()))
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[pallet::event]
|
|
pub enum Event<T: Config> {
|
|
/// Contract deployed by address at the specified address.
|
|
Instantiated { deployer: T::AccountId, contract: T::AccountId },
|
|
|
|
/// Contract has been removed.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// The only way for a contract to be removed and emitting this event is by calling
|
|
/// `seal_terminate`.
|
|
Terminated {
|
|
/// The contract that was terminated.
|
|
contract: T::AccountId,
|
|
/// The account that received the contracts remaining balance
|
|
beneficiary: T::AccountId,
|
|
},
|
|
|
|
/// Code with the specified hash has been stored.
|
|
CodeStored { code_hash: T::Hash, deposit_held: BalanceOf<T>, uploader: T::AccountId },
|
|
|
|
/// A custom event emitted by the contract.
|
|
ContractEmitted {
|
|
/// The contract that emitted the event.
|
|
contract: T::AccountId,
|
|
/// Data supplied by the contract. Metadata generated during contract compilation
|
|
/// is needed to decode it.
|
|
data: Vec<u8>,
|
|
},
|
|
|
|
/// A code with the specified hash was removed.
|
|
CodeRemoved { code_hash: T::Hash, deposit_released: BalanceOf<T>, remover: T::AccountId },
|
|
|
|
/// A contract's code was updated.
|
|
ContractCodeUpdated {
|
|
/// The contract that has been updated.
|
|
contract: T::AccountId,
|
|
/// New code hash that was set for the contract.
|
|
new_code_hash: T::Hash,
|
|
/// Previous code hash of the contract.
|
|
old_code_hash: T::Hash,
|
|
},
|
|
|
|
/// A contract was called either by a plain account or another contract.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Please keep in mind that like all events this is only emitted for successful
|
|
/// calls. This is because on failure all storage changes including events are
|
|
/// rolled back.
|
|
Called {
|
|
/// The caller of the `contract`.
|
|
caller: Origin<T>,
|
|
/// The contract that was called.
|
|
contract: T::AccountId,
|
|
},
|
|
|
|
/// A contract delegate called a code hash.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Please keep in mind that like all events this is only emitted for successful
|
|
/// calls. This is because on failure all storage changes including events are
|
|
/// rolled back.
|
|
DelegateCalled {
|
|
/// The contract that performed the delegate call and hence in whose context
|
|
/// the `code_hash` is executed.
|
|
contract: T::AccountId,
|
|
/// The code hash that was delegate called.
|
|
code_hash: CodeHash<T>,
|
|
},
|
|
|
|
/// Some funds have been transferred and held as storage deposit.
|
|
StorageDepositTransferredAndHeld {
|
|
from: T::AccountId,
|
|
to: T::AccountId,
|
|
amount: BalanceOf<T>,
|
|
},
|
|
|
|
/// Some storage deposit funds have been transferred and released.
|
|
StorageDepositTransferredAndReleased {
|
|
from: T::AccountId,
|
|
to: T::AccountId,
|
|
amount: BalanceOf<T>,
|
|
},
|
|
}
|
|
|
|
#[pallet::error]
|
|
pub enum Error<T> {
|
|
/// Invalid schedule supplied, e.g. with zero weight of a basic operation.
|
|
InvalidSchedule,
|
|
/// Invalid combination of flags supplied to `seal_call` or `seal_delegate_call`.
|
|
InvalidCallFlags,
|
|
/// The executed contract exhausted its gas limit.
|
|
OutOfGas,
|
|
/// The output buffer supplied to a contract API call was too small.
|
|
OutputBufferTooSmall,
|
|
/// Performing the requested transfer failed. Probably because there isn't enough
|
|
/// free balance in the sender's account.
|
|
TransferFailed,
|
|
/// Performing a call was denied because the calling depth reached the limit
|
|
/// of what is specified in the schedule.
|
|
MaxCallDepthReached,
|
|
/// No contract was found at the specified address.
|
|
ContractNotFound,
|
|
/// The code supplied to `instantiate_with_code` exceeds the limit specified in the
|
|
/// current schedule.
|
|
CodeTooLarge,
|
|
/// No code could be found at the supplied code hash.
|
|
CodeNotFound,
|
|
/// No code info could be found at the supplied code hash.
|
|
CodeInfoNotFound,
|
|
/// A buffer outside of sandbox memory was passed to a contract API function.
|
|
OutOfBounds,
|
|
/// Input passed to a contract API function failed to decode as expected type.
|
|
DecodingFailed,
|
|
/// Contract trapped during execution.
|
|
ContractTrapped,
|
|
/// The size defined in `T::MaxValueSize` was exceeded.
|
|
ValueTooLarge,
|
|
/// Termination of a contract is not allowed while the contract is already
|
|
/// on the call stack. Can be triggered by `seal_terminate`.
|
|
TerminatedWhileReentrant,
|
|
/// `seal_call` forwarded this contracts input. It therefore is no longer available.
|
|
InputForwarded,
|
|
/// The subject passed to `seal_random` exceeds the limit.
|
|
RandomSubjectTooLong,
|
|
/// The amount of topics passed to `seal_deposit_events` exceeds the limit.
|
|
TooManyTopics,
|
|
/// The chain does not provide a chain extension. Calling the chain extension results
|
|
/// in this error. Note that this usually shouldn't happen as deploying such contracts
|
|
/// is rejected.
|
|
NoChainExtension,
|
|
/// A contract with the same AccountId already exists.
|
|
DuplicateContract,
|
|
/// A contract self destructed in its constructor.
|
|
///
|
|
/// This can be triggered by a call to `seal_terminate`.
|
|
TerminatedInConstructor,
|
|
/// A call tried to invoke a contract that is flagged as non-reentrant.
|
|
/// The only other cause is that a call from a contract into the runtime tried to call back
|
|
/// into `pallet-contracts`. This would make the whole pallet reentrant with regard to
|
|
/// contract code execution which is not supported.
|
|
ReentranceDenied,
|
|
/// Origin doesn't have enough balance to pay the required storage deposits.
|
|
StorageDepositNotEnoughFunds,
|
|
/// More storage was created than allowed by the storage deposit limit.
|
|
StorageDepositLimitExhausted,
|
|
/// Code removal was denied because the code is still in use by at least one contract.
|
|
CodeInUse,
|
|
/// The contract ran to completion but decided to revert its storage changes.
|
|
/// Please note that this error is only returned from extrinsics. When called directly
|
|
/// or via RPC an `Ok` will be returned. In this case the caller needs to inspect the flags
|
|
/// to determine whether a reversion has taken place.
|
|
ContractReverted,
|
|
/// The contract's code was found to be invalid during validation.
|
|
///
|
|
/// The most likely cause of this is that an API was used which is not supported by the
|
|
/// node. This happens if an older node is used with a new version of ink!. Try updating
|
|
/// your node to the newest available version.
|
|
///
|
|
/// A more detailed error can be found on the node console if debug messages are enabled
|
|
/// by supplying `-lruntime::contracts=debug`.
|
|
CodeRejected,
|
|
/// An indetermistic code was used in a context where this is not permitted.
|
|
Indeterministic,
|
|
/// A pending migration needs to complete before the extrinsic can be called.
|
|
MigrationInProgress,
|
|
/// Migrate dispatch call was attempted but no migration was performed.
|
|
NoMigrationPerformed,
|
|
/// The contract has reached its maximum number of delegate dependencies.
|
|
MaxDelegateDependenciesReached,
|
|
/// The dependency was not found in the contract's delegate dependencies.
|
|
DelegateDependencyNotFound,
|
|
/// The contract already depends on the given delegate dependency.
|
|
DelegateDependencyAlreadyExists,
|
|
/// Can not add a delegate dependency to the code hash of the contract itself.
|
|
CannotAddSelfAsDelegateDependency,
|
|
}
|
|
|
|
/// A reason for the pallet contracts placing a hold on funds.
|
|
#[pallet::composite_enum]
|
|
pub enum HoldReason {
|
|
/// The Pallet has reserved it for storing code on-chain.
|
|
CodeUploadDepositReserve,
|
|
/// The Pallet has reserved it for storage deposit.
|
|
StorageDepositReserve,
|
|
}
|
|
|
|
/// A mapping from a contract's code hash to its code.
|
|
#[pallet::storage]
|
|
pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, CodeVec<T>>;
|
|
|
|
/// A mapping from a contract's code hash to its code info.
|
|
#[pallet::storage]
|
|
pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, CodeHash<T>, CodeInfo<T>>;
|
|
|
|
/// This is a **monotonic** counter incremented on contract instantiation.
|
|
///
|
|
/// This is used in order to generate unique trie ids for contracts.
|
|
/// The trie id of a new contract is calculated from hash(account_id, nonce).
|
|
/// The nonce is required because otherwise the following sequence would lead to
|
|
/// a possible collision of storage:
|
|
///
|
|
/// 1. Create a new contract.
|
|
/// 2. Terminate the contract.
|
|
/// 3. Immediately recreate the contract with the same account_id.
|
|
///
|
|
/// This is bad because the contents of a trie are deleted lazily and there might be
|
|
/// storage of the old instantiation still in it when the new contract is created. Please
|
|
/// note that we can't replace the counter by the block number because the sequence above
|
|
/// can happen in the same block. We also can't keep the account counter in memory only
|
|
/// because storage is the only way to communicate across different extrinsics in the
|
|
/// same block.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// Do not use it to determine the number of contracts. It won't be decremented if
|
|
/// a contract is destroyed.
|
|
#[pallet::storage]
|
|
pub(crate) type Nonce<T: Config> = StorageValue<_, u64, ValueQuery>;
|
|
|
|
/// The code associated with a given account.
|
|
///
|
|
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
|
|
#[pallet::storage]
|
|
pub(crate) type ContractInfoOf<T: Config> =
|
|
StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
|
|
|
|
/// Evicted contracts that await child trie deletion.
|
|
///
|
|
/// 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_idle`.
|
|
#[pallet::storage]
|
|
pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
|
|
|
|
/// A pair of monotonic counters used to track the latest contract marked for deletion
|
|
/// and the latest deleted contract in queue.
|
|
#[pallet::storage]
|
|
pub(crate) type DeletionQueueCounter<T: Config> =
|
|
StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
|
|
|
|
/// A migration can span across multiple blocks. This storage defines a cursor to track the
|
|
/// progress of the migration, enabling us to resume from the last completed position.
|
|
#[pallet::storage]
|
|
pub(crate) type MigrationInProgress<T: Config> =
|
|
StorageValue<_, migration::Cursor, OptionQuery>;
|
|
}
|
|
|
|
/// The type of origins supported by the contracts pallet.
|
|
#[derive(Clone, Encode, Decode, PartialEq, TypeInfo, RuntimeDebugNoBound)]
|
|
pub enum Origin<T: Config> {
|
|
Root,
|
|
Signed(T::AccountId),
|
|
}
|
|
|
|
impl<T: Config> Origin<T> {
|
|
/// Creates a new Signed Caller from an AccountId.
|
|
pub fn from_account_id(account_id: T::AccountId) -> Self {
|
|
Origin::Signed(account_id)
|
|
}
|
|
/// Creates a new Origin from a `RuntimeOrigin`.
|
|
pub fn from_runtime_origin(o: OriginFor<T>) -> Result<Self, DispatchError> {
|
|
match o.into() {
|
|
Ok(RawOrigin::Root) => Ok(Self::Root),
|
|
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> {
|
|
origin: Origin<T>,
|
|
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,
|
|
}
|
|
|
|
/// Reference to an existing code hash or a new wasm module.
|
|
enum WasmCode<T: Config> {
|
|
Wasm(WasmBlob<T>),
|
|
CodeHash(CodeHash<T>),
|
|
}
|
|
|
|
/// Input specific to a contract instantiation invocation.
|
|
struct InstantiateInput<T: Config> {
|
|
code: WasmCode<T>,
|
|
salt: Vec<u8>,
|
|
}
|
|
|
|
/// Determines whether events should be collected during execution.
|
|
#[derive(
|
|
Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
|
|
)]
|
|
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(
|
|
Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
|
|
)]
|
|
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>: Sized {
|
|
/// 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;
|
|
let mut storage_meter =
|
|
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, WasmBlob<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,
|
|
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 executable = match self.code {
|
|
WasmCode::Wasm(module) => module,
|
|
WasmCode::CodeHash(code_hash) => WasmBlob::from_storage(code_hash, &mut gas_meter)?,
|
|
};
|
|
|
|
let contract_origin = Origin::from_account_id(origin.clone());
|
|
let mut storage_meter =
|
|
StorageMeter::new(&contract_origin, common.storage_deposit_limit, common.value)?;
|
|
let CommonInput { value, data, debug_message, .. } = common;
|
|
let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
|
|
origin.clone(),
|
|
executable,
|
|
&mut gas_meter,
|
|
&mut storage_meter,
|
|
&schedule,
|
|
value,
|
|
data.clone(),
|
|
&salt,
|
|
debug_message,
|
|
);
|
|
|
|
storage_deposit = storage_meter.try_into_deposit(&contract_origin)?;
|
|
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.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// 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>,
|
|
gas_limit: Weight,
|
|
storage_deposit_limit: Option<BalanceOf<T>>,
|
|
data: Vec<u8>,
|
|
debug: DebugInfo,
|
|
collect_events: CollectEvents,
|
|
determinism: Determinism,
|
|
) -> 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);
|
|
let common = CommonInput {
|
|
origin,
|
|
value,
|
|
data,
|
|
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
|
|
};
|
|
|
|
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(),
|
|
events,
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
///
|
|
/// 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>,
|
|
gas_limit: Weight,
|
|
mut 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
|
|
};
|
|
// 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
|
|
}
|
|
};
|
|
|
|
let (code, upload_deposit): (WasmCode<T>, BalanceOf<T>) = match code {
|
|
Code::Upload(code) => {
|
|
let result = Self::try_upload_code(
|
|
origin.clone(),
|
|
code,
|
|
storage_deposit_limit.map(Into::into),
|
|
Determinism::Enforced,
|
|
debug_message.as_mut(),
|
|
);
|
|
|
|
let (module, deposit) = match result {
|
|
Ok(result) => result,
|
|
Err(error) =>
|
|
return ContractResult {
|
|
gas_consumed: Zero::zero(),
|
|
gas_required: Zero::zero(),
|
|
storage_deposit: Default::default(),
|
|
debug_message: debug_message.unwrap_or(Default::default()).into(),
|
|
result: Err(error),
|
|
events: events(),
|
|
},
|
|
};
|
|
|
|
storage_deposit_limit =
|
|
storage_deposit_limit.map(|l| l.saturating_sub(deposit.into()));
|
|
(WasmCode::Wasm(module), deposit)
|
|
},
|
|
Code::Existing(hash) => (WasmCode::CodeHash(hash), Default::default()),
|
|
};
|
|
|
|
let common = CommonInput {
|
|
origin: Origin::from_account_id(origin),
|
|
value,
|
|
data,
|
|
gas_limit,
|
|
storage_deposit_limit,
|
|
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
|
|
.saturating_add(&StorageDeposit::Charge(upload_deposit)),
|
|
debug_message: debug_message.unwrap_or_default().to_vec(),
|
|
events: events(),
|
|
}
|
|
}
|
|
|
|
/// 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>> {
|
|
Migration::<T>::ensure_migrated()?;
|
|
let (module, deposit) =
|
|
Self::try_upload_code(origin, code, storage_deposit_limit, determinism, None)?;
|
|
Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
|
|
}
|
|
|
|
/// Uploads new code and returns the Wasm blob and deposit amount collected.
|
|
fn try_upload_code(
|
|
origin: T::AccountId,
|
|
code: Vec<u8>,
|
|
storage_deposit_limit: Option<BalanceOf<T>>,
|
|
determinism: Determinism,
|
|
mut debug_message: Option<&mut DebugBufferVec<T>>,
|
|
) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
|
|
let schedule = T::Schedule::get();
|
|
let mut module =
|
|
WasmBlob::from_code(code, &schedule, origin, determinism).map_err(|(err, msg)| {
|
|
debug_message.as_mut().map(|d| d.try_extend(msg.bytes()));
|
|
err
|
|
})?;
|
|
let deposit = module.store_code()?;
|
|
if let Some(storage_deposit_limit) = storage_deposit_limit {
|
|
ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
|
|
}
|
|
|
|
Ok((module, deposit))
|
|
}
|
|
|
|
/// 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)?;
|
|
|
|
let maybe_value = contract_info.read(
|
|
&Key::<T>::try_from_var(key)
|
|
.map_err(|_| ContractAccessError::KeyDecodingFailed)?
|
|
.into(),
|
|
);
|
|
Ok(maybe_value)
|
|
}
|
|
|
|
/// 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],
|
|
) -> T::AccountId {
|
|
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>> {
|
|
ContractInfo::<T>::load_code_hash(account)
|
|
}
|
|
|
|
/// Store code for benchmarks which does not validate 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();
|
|
WasmBlob::<T>::from_code_unchecked(code, &schedule, owner)?.store_code()?;
|
|
Ok(())
|
|
}
|
|
|
|
/// 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>,
|
|
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;
|
|
}
|
|
}
|