1// This file is part of Substrate.23// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.4// SPDX-License-Identifier: Apache-2.056// Licensed under the Apache License, Version 2.0 (the "License");7// you may not use this file except in compliance with the License.8// You may obtain a copy of the License at9//10// http://www.apache.org/licenses/LICENSE-2.011//12// Unless required by applicable law or agreed to in writing, software13// distributed under the License is distributed on an "AS IS" BASIS,14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15// See the License for the specific language governing permissions and16// limitations under the License.1718//! # Contract Pallet19//!20//! The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.21//!22//! - [`Config`]23//! - [`Call`]24//!25//! ## Overview26//!27//! This module extends accounts based on the [`Currency`] trait to have smart-contract functionality. It can28//! be used with other modules that implement accounts based on [`Currency`]. These "smart-contract accounts"29//! have the ability to instantiate smart-contracts and make calls to other contract and non-contract accounts.30//!31//! The smart-contract code is stored once in a code cache, and later retrievable via its hash.32//! This means that multiple smart-contracts can be instantiated from the same hash, without replicating33//! the code each time.34//!35//! When a smart-contract is called, its associated code is retrieved via the code hash and gets executed.36//! This call can alter the storage entries of the smart-contract account, instantiate new smart-contracts,37//! or call other smart-contracts.38//!39//! Finally, when an account is reaped, its associated code and storage of the smart-contract account40//! will also be deleted.41//!42//! ### Gas43//!44//! Senders must specify a gas limit with every call, as all instructions invoked by the smart-contract require gas.45//! Unused gas is refunded after the call, regardless of the execution outcome.46//!47//! If the gas limit is reached, then all calls and state changes (including balance transfers) are only48//! reverted at the current call's contract level. For example, if contract A calls B and B runs out of gas mid-call,49//! then all of B's calls are reverted. Assuming correct error handling by contract A, A's other calls and state50//! changes still persist.51//!52//! ### Notable Scenarios53//!54//! Contract call failures are not always cascading. When failures occur in a sub-call, they do not "bubble up",55//! and the call will only revert at the specific contract level. For example, if contract A calls contract B, and B56//! fails, A can decide how to handle that failure, either proceeding or reverting A's changes.57//!58//! ## Interface59//!60//! ### Dispatchable functions61//!62//! * [`Pallet::update_schedule`] -63//! ([Root Origin](https://substrate.dev/docs/en/knowledgebase/runtime/origin) Only) -64//! Set a new [`Schedule`].65//! * [`Pallet::instantiate_with_code`] - Deploys a new contract from the supplied wasm binary,66//! optionally transferring67//! some balance. This instantiates a new smart contract account with the supplied code and68//! calls its constructor to initialize the contract.69//! * [`Pallet::instantiate`] - The same as `instantiate_with_code` but instead of uploading new70//! code an existing `code_hash` is supplied.71//! * [`Pallet::call`] - Makes a call to an account, optionally transferring some balance.72//! * [`Pallet::claim_surcharge`] - Evict a contract that cannot pay rent anymore.73//!74//! ## Usage75//!76//! The Contract module is a work in progress. The following examples show how this Contract module77//! can be used to instantiate and call contracts.78//!79//! * [`ink`](https://github.com/paritytech/ink) is80//! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing81//! WebAssembly based smart contracts in the Rust programming language. This is a work in progress.82//!83//! ## Related Modules84//!85//! * [Balances](../pallet_balances/index.html)8687#![cfg_attr(not(feature = "std"), no_std)]88#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "512")]8990#[macro_use]91mod gas;92mod benchmarking;93mod exec;94mod migration;95mod rent;96mod schedule;97mod storage;98mod wasm;99100pub mod chain_extension;101pub mod weights;102103#[cfg(test)]104mod tests;105106pub use crate::{pallet::*, schedule::Schedule};107use crate::{108 gas::GasMeter,109 exec::{ExecutionContext, Executable},110 rent::Rent,111 storage::{Storage, DeletedContract, ContractInfo, AliveContractInfo, TombstoneContractInfo},112 weights::WeightInfo,113 wasm::PrefabWasmModule,114};115use sp_core::{Bytes, crypto::UncheckedFrom};116use sp_std::prelude::*;117use sp_runtime::{118 traits::{Hash, StaticLookup, Convert, Saturating, Zero},119 Perbill,120};121use frame_support::{122 traits::{OnUnbalanced, Currency, Get, Time, Randomness},123 weights::{Weight, PostDispatchInfo, WithPostDispatchInfo},124};125use frame_system::Pallet as System;126use pallet_contracts_primitives::{127 RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult,128 ContractInstantiateResult, Code, InstantiateReturnValue,129};130131type CodeHash<T> = <T as frame_system::Config>::Hash;132type TrieId = Vec<u8>;133type BalanceOf<T> =134 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;135type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<136 <T as frame_system::Config>::AccountId,137>>::NegativeImbalance;138139#[frame_support::pallet]140pub mod pallet {141 use frame_support::pallet_prelude::*;142 use frame_system::pallet_prelude::*;143 use super::*;144145 #[pallet::config]146 pub trait Config: frame_system::Config {147 /// The time implementation used to supply timestamps to conntracts through `seal_now`.148 type Time: Time;149150 /// The generator used to supply randomness to contracts through `seal_random`.151 type Randomness: Randomness<Self::Hash, Self::BlockNumber>;152153 /// The currency in which fees are paid and contract balances are held.154 type Currency: Currency<Self::AccountId>;155156 /// The overarching event type.157 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;158159 /// Handler for rent payments.160 type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;161162 /// Number of block delay an extrinsic claim surcharge has.163 ///164 /// When claim surcharge is called by an extrinsic the rent is checked165 /// for current_block - delay166 #[pallet::constant]167 type SignedClaimHandicap: Get<Self::BlockNumber>;168169 /// The minimum amount required to generate a tombstone.170 #[pallet::constant]171 type TombstoneDeposit: Get<BalanceOf<Self>>;172173 /// The balance every contract needs to deposit to stay alive indefinitely.174 ///175 /// This is different from the [`Self::TombstoneDeposit`] because this only needs to be176 /// deposited while the contract is alive. Costs for additional storage are added to177 /// this base cost.178 ///179 /// This is a simple way to ensure that contracts with empty storage eventually get deleted by180 /// making them pay rent. This creates an incentive to remove them early in order to save rent.181 #[pallet::constant]182 type DepositPerContract: Get<BalanceOf<Self>>;183184 /// The balance a contract needs to deposit per storage byte to stay alive indefinitely.185 ///186 /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day,187 /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent.188 /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000,189 /// then it would pay 500 BU/day.190 #[pallet::constant]191 type DepositPerStorageByte: Get<BalanceOf<Self>>;192193 /// The balance a contract needs to deposit per storage item to stay alive indefinitely.194 ///195 /// It works the same as [`Self::DepositPerStorageByte`] but for storage items.196 #[pallet::constant]197 type DepositPerStorageItem: Get<BalanceOf<Self>>;198199 /// The fraction of the deposit that should be used as rent per block.200 ///201 /// When a contract hasn't enough balance deposited to stay alive indefinitely it needs202 /// to pay per block for the storage it consumes that is not covered by the deposit.203 /// This determines how high this rent payment is per block as a fraction of the deposit.204 #[pallet::constant]205 type RentFraction: Get<Perbill>;206207 /// Reward that is received by the party whose touch has led208 /// to removal of a contract.209 #[pallet::constant]210 type SurchargeReward: Get<BalanceOf<Self>>;211212 /// The maximum nesting level of a call/instantiate stack.213 #[pallet::constant]214 type MaxDepth: Get<u32>;215216 /// The maximum size of a storage value and event payload in bytes.217 #[pallet::constant]218 type MaxValueSize: Get<u32>;219220 /// Used to answer contracts' queries regarding the current weight price. This is **not**221 /// used to calculate the actual fee and is only for informational purposes.222 type WeightPrice: Convert<Weight, BalanceOf<Self>>;223224 /// Describes the weights of the dispatchables of this module and is also used to225 /// construct a default cost schedule.226 type WeightInfo: WeightInfo;227228 /// Type that allows the runtime authors to add new host functions for a contract to call.229 type ChainExtension: chain_extension::ChainExtension<Self>;230231 /// The maximum number of tries that can be queued for deletion.232 #[pallet::constant]233 type DeletionQueueDepth: Get<u32>;234235 /// The maximum amount of weight that can be consumed per block for lazy trie removal.236 #[pallet::constant]237 type DeletionWeightLimit: Get<Weight>;238239 /// The maximum length of a contract code in bytes. This limit applies to the instrumented240 /// version of the code. Therefore `instantiate_with_code` can fail even when supplying241 /// a wasm binary below this maximum size.242 #[pallet::constant]243 type MaxCodeSize: Get<u32>;244 }245246 #[pallet::pallet]247 pub struct Pallet<T>(PhantomData<T>);248249 #[pallet::hooks]250 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>251 where252 T::AccountId: UncheckedFrom<T::Hash>,253 T::AccountId: AsRef<[u8]>,254 {255 fn on_initialize(_block: T::BlockNumber) -> Weight {256 // We do not want to go above the block limit and rather avoid lazy deletion257 // in that case. This should only happen on runtime upgrades.258 let weight_limit = T::BlockWeights::get()259 .max_block260 .saturating_sub(System::<T>::block_weight().total())261 .min(T::DeletionWeightLimit::get());262 Storage::<T>::process_deletion_queue_batch(weight_limit)263 .saturating_add(T::WeightInfo::on_initialize())264 }265266 fn on_runtime_upgrade() -> Weight {267 migration::migrate::<T>()268 }269 }270271 #[pallet::call]272 impl<T: Config> Pallet<T>273 where274 T::AccountId: UncheckedFrom<T::Hash>,275 T::AccountId: AsRef<[u8]>,276 {277 /// Updates the schedule for metering contracts.278 ///279 /// The schedule's version cannot be less than the version of the stored schedule.280 /// If a schedule does not change the instruction weights the version does not281 /// need to be increased. Therefore we allow storing a schedule that has the same282 /// version as the stored one.283 #[pallet::weight(T::WeightInfo::update_schedule())]284 pub fn update_schedule(285 origin: OriginFor<T>,286 schedule: Schedule<T>,287 ) -> DispatchResultWithPostInfo {288 ensure_root(origin)?;289 if <CurrentSchedule<T>>::get().version > schedule.version {290 Err(Error::<T>::InvalidScheduleVersion)?291 }292 Self::deposit_event(Event::ScheduleUpdated(schedule.version));293 CurrentSchedule::put(schedule);294 Ok(().into())295 }296297 /// Makes a call to an account, optionally transferring some balance.298 ///299 /// * If the account is a smart-contract account, the associated code will be300 /// executed and any value will be transferred.301 /// * If the account is a regular account, any value will be transferred.302 /// * If no account exists and the call value is not less than `existential_deposit`,303 /// a regular account will be created and any value will be transferred.304 #[pallet::weight(T::WeightInfo::call(T::MaxCodeSize::get() / 1024).saturating_add(*gas_limit))]305 pub fn call(306 origin: OriginFor<T>,307 dest: <T::Lookup as StaticLookup>::Source,308 #[pallet::compact] value: BalanceOf<T>,309 #[pallet::compact] gas_limit: Weight,310 data: Vec<u8>,311 ) -> DispatchResultWithPostInfo {312 let origin = ensure_signed(origin)?;313 let dest = T::Lookup::lookup(dest)?;314 let mut gas_meter = GasMeter::new(gas_limit);315 let schedule = <CurrentSchedule<T>>::get();316 let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);317 let (result, code_len) = match ctx.call(dest, value, &mut gas_meter, data) {318 Ok((output, len)) => (Ok(output), len),319 Err((err, len)) => (Err(err), len),320 };321 gas_meter.into_dispatch_result(result, T::WeightInfo::call(code_len / 1024))322 }323324 /// Instantiates a new contract from the supplied `code` optionally transferring325 /// some balance.326 ///327 /// This is the only function that can deploy new code to the chain.328 ///329 /// # Parameters330 ///331 /// * `endowment`: The balance to transfer from the `origin` to the newly created contract.332 /// * `gas_limit`: The gas limit enforced when executing the constructor.333 /// * `code`: The contract code to deploy in raw bytes.334 /// * `data`: The input data to pass to the contract constructor.335 /// * `salt`: Used for the address derivation. See [`Pallet::contract_address`].336 ///337 /// Instantiation is executed as follows:338 ///339 /// - The supplied `code` is instrumented, deployed, and a `code_hash` is created for that code.340 /// - If the `code_hash` already exists on the chain the underlying `code` will be shared.341 /// - The destination address is computed based on the sender, code_hash and the salt.342 /// - The smart-contract account is created at the computed address.343 /// - The `endowment` is transferred to the new account.344 /// - The `deploy` function is executed in the context of the newly-created account.345 #[pallet::weight(346 T::WeightInfo::instantiate_with_code(347 code.len() as u32 / 1024,348 salt.len() as u32 / 1024,349 )350 .saturating_add(*gas_limit)351 )]352 pub fn instantiate_with_code(353 origin: OriginFor<T>,354 #[pallet::compact] endowment: BalanceOf<T>,355 #[pallet::compact] gas_limit: Weight,356 code: Vec<u8>,357 data: Vec<u8>,358 salt: Vec<u8>,359 ) -> DispatchResultWithPostInfo {360 let origin = ensure_signed(origin)?;361 let code_len = code.len() as u32;362 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);363 let mut gas_meter = GasMeter::new(gas_limit);364 let schedule = <CurrentSchedule<T>>::get();365 let executable = PrefabWasmModule::from_code(code, &schedule)?;366 let code_len = executable.code_len();367 ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);368 let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);369 let result = ctx370 .instantiate(endowment, &mut gas_meter, executable, data, &salt)371 .map(|(_address, output)| output);372 gas_meter.into_dispatch_result(373 result,374 T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024),375 )376 }377378 /// Instantiates a contract from a previously deployed wasm binary.379 ///380 /// This function is identical to [`Self::instantiate_with_code`] but without the381 /// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary382 /// must be supplied.383 #[pallet::weight(384 T::WeightInfo::instantiate(T::MaxCodeSize::get() / 1024, salt.len() as u32 / 1024)385 .saturating_add(*gas_limit)386 )]387 pub fn instantiate(388 origin: OriginFor<T>,389 #[pallet::compact] endowment: BalanceOf<T>,390 #[pallet::compact] gas_limit: Weight,391 code_hash: CodeHash<T>,392 data: Vec<u8>,393 salt: Vec<u8>,394 ) -> DispatchResultWithPostInfo {395 let origin = ensure_signed(origin)?;396 let mut gas_meter = GasMeter::new(gas_limit);397 let schedule = <CurrentSchedule<T>>::get();398 let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;399 let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);400 let code_len = executable.code_len();401 let result = ctx402 .instantiate(endowment, &mut gas_meter, executable, data, &salt)403 .map(|(_address, output)| output);404 gas_meter.into_dispatch_result(405 result,406 T::WeightInfo::instantiate(code_len / 1024, salt.len() as u32 / 1024),407 )408 }409410 /// Allows block producers to claim a small reward for evicting a contract. If a block411 /// producer fails to do so, a regular users will be allowed to claim the reward.412 ///413 /// In case of a successful eviction no fees are charged from the sender. However, the414 /// reward is capped by the total amount of rent that was payed by the contract while415 /// it was alive.416 ///417 /// If contract is not evicted as a result of this call, [`Error::ContractNotEvictable`]418 /// is returned and the sender is not eligible for the reward.419 #[pallet::weight(T::WeightInfo::claim_surcharge(T::MaxCodeSize::get() / 1024))]420 pub fn claim_surcharge(421 origin: OriginFor<T>,422 dest: T::AccountId,423 aux_sender: Option<T::AccountId>,424 ) -> DispatchResultWithPostInfo {425 let origin = origin.into();426 let (signed, rewarded) = match (origin, aux_sender) {427 (Ok(frame_system::RawOrigin::Signed(account)), None) => (true, account),428 (Ok(frame_system::RawOrigin::None), Some(aux_sender)) => (false, aux_sender),429 _ => Err(Error::<T>::InvalidSurchargeClaim)?,430 };431432 // Add some advantage for block producers (who send unsigned extrinsics) by433 // adding a handicap: for signed extrinsics we use a slightly older block number434 // for the eviction check. This can be viewed as if we pushed regular users back in past.435 let handicap = if signed {436 T::SignedClaimHandicap::get()437 } else {438 Zero::zero()439 };440441 // If poking the contract has lead to eviction of the contract, give out the rewards.442 match Rent::<T, PrefabWasmModule<T>>::try_eviction(&dest, handicap)? {443 (Some(rent_payed), code_len) => T::Currency::deposit_into_existing(444 &rewarded,445 T::SurchargeReward::get().min(rent_payed),446 )447 .map(|_| PostDispatchInfo {448 actual_weight: Some(T::WeightInfo::claim_surcharge(code_len / 1024)),449 pays_fee: Pays::No,450 })451 .map_err(Into::into),452 (None, code_len) => Err(Error::<T>::ContractNotEvictable453 .with_weight(T::WeightInfo::claim_surcharge(code_len / 1024))),454 }455 }456 }457458 #[pallet::event]459 #[pallet::generate_deposit(pub(super) fn deposit_event)]460 #[pallet::metadata(T::AccountId = "AccountId", T::Hash = "Hash", BalanceOf<T> = "Balance")]461 pub enum Event<T: Config> {462 /// Contract deployed by address at the specified address. \[deployer, contract\]463 Instantiated(T::AccountId, T::AccountId),464465 /// Contract has been evicted and is now in tombstone state. \[contract\]466 Evicted(T::AccountId),467468 /// Contract has been terminated without leaving a tombstone.469 /// \[contract, beneficiary\]470 ///471 /// # Params472 ///473 /// - `contract`: The contract that was terminated.474 /// - `beneficiary`: The account that received the contracts remaining balance.475 ///476 /// # Note477 ///478 /// The only way for a contract to be removed without a tombstone and emitting479 /// this event is by calling `seal_terminate`.480 Terminated(T::AccountId, T::AccountId),481482 /// Restoration of a contract has been successful.483 /// \[restorer, dest, code_hash, rent_allowance\]484 ///485 /// # Params486 ///487 /// - `restorer`: Account ID of the restoring contract.488 /// - `dest`: Account ID of the restored contract.489 /// - `code_hash`: Code hash of the restored contract.490 /// - `rent_allowance`: Rent allowance of the restored contract.491 Restored(T::AccountId, T::AccountId, T::Hash, BalanceOf<T>),492493 /// Code with the specified hash has been stored. \[code_hash\]494 CodeStored(T::Hash),495496 /// Triggered when the current schedule is updated.497 /// \[version\]498 ///499 /// # Params500 ///501 /// - `version`: The version of the newly set schedule.502 ScheduleUpdated(u32),503504 /// A custom event emitted by the contract.505 /// \[contract, data\]506 ///507 /// # Params508 ///509 /// - `contract`: The contract that emitted the event.510 /// - `data`: Data supplied by the contract. Metadata generated during contract511 /// compilation is needed to decode it.512 ContractEmitted(T::AccountId, Vec<u8>),513514 /// A code with the specified hash was removed.515 /// \[code_hash\]516 ///517 /// This happens when the last contract that uses this code hash was removed or evicted.518 CodeRemoved(T::Hash),519 }520521 #[pallet::error]522 pub enum Error<T> {523 /// A new schedule must have a greater version than the current one.524 InvalidScheduleVersion,525 /// An origin must be signed or inherent and auxiliary sender only provided on inherent.526 InvalidSurchargeClaim,527 /// Cannot restore from nonexisting or tombstone contract.528 InvalidSourceContract,529 /// Cannot restore to nonexisting or alive contract.530 InvalidDestinationContract,531 /// Tombstones don't match.532 InvalidTombstone,533 /// An origin TrieId written in the current block.534 InvalidContractOrigin,535 /// The executed contract exhausted its gas limit.536 OutOfGas,537 /// The output buffer supplied to a contract API call was too small.538 OutputBufferTooSmall,539 /// Performing the requested transfer would have brought the contract below540 /// the subsistence threshold. No transfer is allowed to do this in order to allow541 /// for a tombstone to be created. Use `seal_terminate` to remove a contract without542 /// leaving a tombstone behind.543 BelowSubsistenceThreshold,544 /// The newly created contract is below the subsistence threshold after executing545 /// its contructor. No contracts are allowed to exist below that threshold.546 NewContractNotFunded,547 /// Performing the requested transfer failed for a reason originating in the548 /// chosen currency implementation of the runtime. Most probably the balance is549 /// too low or locks are placed on it.550 TransferFailed,551 /// Performing a call was denied because the calling depth reached the limit552 /// of what is specified in the schedule.553 MaxCallDepthReached,554 /// The contract that was called is either no contract at all (a plain account)555 /// or is a tombstone.556 NotCallable,557 /// The code supplied to `instantiate_with_code` exceeds the limit specified in the558 /// current schedule.559 CodeTooLarge,560 /// No code could be found at the supplied code hash.561 CodeNotFound,562 /// A buffer outside of sandbox memory was passed to a contract API function.563 OutOfBounds,564 /// Input passed to a contract API function failed to decode as expected type.565 DecodingFailed,566 /// Contract trapped during execution.567 ContractTrapped,568 /// The size defined in `T::MaxValueSize` was exceeded.569 ValueTooLarge,570 /// The action performed is not allowed while the contract performing it is already571 /// on the call stack. Those actions are contract self destruction and restoration572 /// of a tombstone.573 ReentranceDenied,574 /// `seal_input` was called twice from the same contract execution context.575 InputAlreadyRead,576 /// The subject passed to `seal_random` exceeds the limit.577 RandomSubjectTooLong,578 /// The amount of topics passed to `seal_deposit_events` exceeds the limit.579 TooManyTopics,580 /// The topics passed to `seal_deposit_events` contains at least one duplicate.581 DuplicateTopics,582 /// The chain does not provide a chain extension. Calling the chain extension results583 /// in this error. Note that this usually shouldn't happen as deploying such contracts584 /// is rejected.585 NoChainExtension,586 /// Removal of a contract failed because the deletion queue is full.587 ///588 /// This can happen when either calling [`Pallet::claim_surcharge`] or `seal_terminate`.589 /// The queue is filled by deleting contracts and emptied by a fixed amount each block.590 /// Trying again during another block is the only way to resolve this issue.591 DeletionQueueFull,592 /// A contract could not be evicted because it has enough balance to pay rent.593 ///594 /// This can be returned from [`Pallet::claim_surcharge`] because the target595 /// contract has enough balance to pay for its rent.596 ContractNotEvictable,597 /// A storage modification exhausted the 32bit type that holds the storage size.598 ///599 /// This can either happen when the accumulated storage in bytes is too large or600 /// when number of storage items is too large.601 StorageExhausted,602 /// A contract with the same AccountId already exists.603 DuplicateContract,604 }605606 /// Current cost schedule for contracts.607 #[pallet::storage]608 pub(crate) type CurrentSchedule<T: Config> = StorageValue<_, Schedule<T>, ValueQuery>;609610 /// A mapping from an original code hash to the original code, untouched by instrumentation.611 #[pallet::storage]612 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;613614 /// A mapping between an original code hash and instrumented wasm code, ready for execution.615 #[pallet::storage]616 pub(crate) type CodeStorage<T: Config> =617 StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;618619 /// The subtrie counter.620 #[pallet::storage]621 pub(crate) type AccountCounter<T: Config> = StorageValue<_, u64, ValueQuery>;622623 /// The code associated with a given account.624 ///625 /// TWOX-NOTE: SAFE since `AccountId` is a secure hash.626 #[pallet::storage]627 pub(crate) type ContractInfoOf<T: Config> =628 StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;629630 /// Evicted contracts that await child trie deletion.631 ///632 /// Child trie deletion is a heavy operation depending on the amount of storage items633 /// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.634 #[pallet::storage]635 pub(crate) type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;636637 #[pallet::genesis_config]638 pub struct GenesisConfig<T: Config> {639 #[doc = "Current cost schedule for contracts."]640 pub current_schedule: Schedule<T>,641 }642643 #[cfg(feature = "std")]644 impl<T: Config> Default for GenesisConfig<T> {645 fn default() -> Self {646 Self {647 current_schedule: Default::default(),648 }649 }650 }651652 #[pallet::genesis_build]653 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {654 fn build(&self) {655 <CurrentSchedule<T>>::put(&self.current_schedule);656 }657 }658}659660impl<T: Config> Pallet<T>661where662 T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,663{664 /// Perform a call to a specified contract.665 ///666 /// This function is similar to [`Self::call`], but doesn't perform any address lookups667 /// and better suitable for calling directly from Rust.668 ///669 /// It returns the execution result and the amount of used weight.670 pub fn bare_call(671 origin: T::AccountId,672 dest: T::AccountId,673 value: BalanceOf<T>,674 gas_limit: Weight,675 input_data: Vec<u8>,676 ) -> ContractExecResult {677 let mut gas_meter = GasMeter::new(gas_limit);678 let schedule = <CurrentSchedule<T>>::get();679 let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);680 let result = ctx.call(dest, value, &mut gas_meter, input_data);681 let gas_consumed = gas_meter.gas_spent();682 ContractExecResult {683 result: result.map(|r| r.0).map_err(|r| r.0.error),684 gas_consumed,685 debug_message: Bytes(Vec::new()).to_vec(),686 }687 }688689 /// Instantiate a new contract.690 ///691 /// This function is similar to [`Self::instantiate`], but doesn't perform any address lookups692 /// and better suitable for calling directly from Rust.693 ///694 /// It returns the execution result, account id and the amount of used weight.695 ///696 /// If `compute_projection` is set to `true` the result also contains the rent projection.697 /// This is optional because some non trivial and stateful work is performed to compute698 /// the projection. See [`Self::rent_projection`].699 pub fn bare_instantiate(700 origin: T::AccountId,701 endowment: BalanceOf<T>,702 gas_limit: Weight,703 code: Code<CodeHash<T>>,704 data: Vec<u8>,705 salt: Vec<u8>,706 compute_projection: bool,707 ) -> ContractInstantiateResult<T::AccountId, T::BlockNumber> {708 let mut gas_meter = GasMeter::new(gas_limit);709 let schedule = <CurrentSchedule<T>>::get();710 let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);711 let executable = match code {712 Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),713 Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),714 };715 let executable = match executable {716 Ok(executable) => executable,717 Err(error) => {718 return ContractInstantiateResult {719 result: Err(error.into()),720 gas_consumed: gas_meter.gas_spent(),721 debug_message: Bytes(Vec::new()).to_vec(),722 }723 }724 };725 let result = ctx726 .instantiate(endowment, &mut gas_meter, executable, data, &salt)727 .and_then(|(account_id, result)| {728 let rent_projection = if compute_projection {729 Some(730 Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)731 .map_err(|_| <Error<T>>::NewContractNotFunded)?,732 )733 } else {734 None735 };736737 Ok(InstantiateReturnValue {738 result,739 account_id,740 rent_projection,741 })742 });743 ContractInstantiateResult {744 result: result.map_err(|e| e.error),745 gas_consumed: gas_meter.gas_spent(),746 debug_message: Bytes(Vec::new()).to_vec(),747 }748 }749750 /// Query storage of a specified contract under a specified key.751 pub fn get_storage(address: T::AccountId, key: [u8; 32]) -> GetStorageResult {752 let contract_info = ContractInfoOf::<T>::get(&address)753 .ok_or(ContractAccessError::DoesntExist)?754 .get_alive()755 .ok_or(ContractAccessError::IsTombstone)?;756757 let maybe_value = Storage::<T>::read(&contract_info.trie_id, &key);758 Ok(maybe_value)759 }760761 /// Query how many blocks the contract stays alive given that the amount endowment762 /// and consumed storage does not change.763 pub fn rent_projection(address: T::AccountId) -> RentProjectionResult<T::BlockNumber> {764 Rent::<T, PrefabWasmModule<T>>::compute_projection(&address)765 }766767 /// Determine the address of a contract,768 ///769 /// This is the address generation function used by contract instantiation. Its result770 /// is only dependend on its inputs. It can therefore be used to reliably predict the771 /// address of a contract. This is akin to the formular of eth's CREATE2 opcode. There772 /// is no CREATE equivalent because CREATE2 is strictly more powerful.773 ///774 /// Formula: `hash(deploying_address ++ code_hash ++ salt)`775 pub fn contract_address(776 deploying_address: &T::AccountId,777 code_hash: &CodeHash<T>,778 salt: &[u8],779 ) -> T::AccountId {780 let buf: Vec<_> = deploying_address781 .as_ref()782 .iter()783 .chain(code_hash.as_ref())784 .chain(salt)785 .cloned()786 .collect();787 UncheckedFrom::unchecked_from(T::Hashing::hash(&buf))788 }789790 /// Subsistence threshold is the extension of the minimum balance (aka existential deposit)791 /// by the tombstone deposit, required for leaving a tombstone.792 ///793 /// Rent or any contract initiated balance transfer mechanism cannot make the balance lower794 /// than the subsistence threshold in order to guarantee that a tombstone is created.795 ///796 /// The only way to completely kill a contract without a tombstone is calling `seal_terminate`.797 pub fn subsistence_threshold() -> BalanceOf<T> {798 T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get())799 }800801 /// The in-memory size in bytes of the data structure associated with each contract.802 ///803 /// The data structure is also put into storage for each contract. The in-storage size804 /// is never larger than the in-memory representation and usually smaller due to compact805 /// encoding and lack of padding.806 ///807 /// # Note808 ///809 /// This returns the in-memory size because the in-storage size (SCALE encoded) cannot810 /// be efficiently determined. Treat this as an upper bound of the in-storage size.811 pub fn contract_info_size() -> u32 {812 sp_std::mem::size_of::<ContractInfo<T>>() as u32813 }814815 /// Store code for benchmarks which does not check nor instrument the code.816 #[cfg(feature = "runtime-benchmarks")]817 fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {818 let schedule = <CurrentSchedule<T>>::get();819 PrefabWasmModule::store_code_unchecked(code, &schedule)?;820 Ok(())821 }822823 /// This exists so that benchmarks can determine the weight of running an instrumentation.824 #[cfg(feature = "runtime-benchmarks")]825 fn reinstrument_module(826 module: &mut PrefabWasmModule<T>,827 schedule: &Schedule<T>,828 ) -> frame_support::dispatch::DispatchResult {829 self::wasm::reinstrument(module, schedule)830 }831}difftreelog
source
pallets/contracts/src/lib.rs31.8 KiBsourcehistory