difftreelog
Remove wrong pallet-contracts
in: master
48 files changed
pallets/contracts/CHANGELOG.mddiffbeforeafterboth--- a/pallets/contracts/CHANGELOG.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-The semantic versioning guarantees cover the interface to the substrate runtime which
-includes this pallet as a dependency. This module will also add storage migrations whenever
-changes require it. Stability with regard to offchain tooling is explicitly excluded from
-this guarantee: For example adding a new field to an in-storage data structure will require
-changes to frontends to properly display it. However, those changes will still be regarded
-as a minor version bump.
-
-The interface provided to smart contracts will adhere to semver with one exception: Even
-major version bumps will be backwards compatible with regard to already deployed contracts.
-In other words: Upgrading this pallet will not break pre-existing contracts.
-
-## [Unreleased]
-
-### Added
-
-- Add new `instantiate` RPC that allows clients to dry-run contract instantiation.
-
-- Make storage and fields of `Schedule` private to the crate.
-[1](https://github.com/paritytech/substrate/pull/8359)
-
-- Add new version of `seal_random` which exposes additional information.
-[1](https://github.com/paritytech/substrate/pull/8329)
-
-- Add `seal_rent_params` contract callable function.
-[1](https://github.com/paritytech/substrate/pull/8231)
-
-## [v3.0.0] 2021-02-25
-
-This version constitutes the first release that brings any stability guarantees (see above).
-
-### Added
-
-- Emit an event when a contract terminates (self-destructs).
-[1](https://github.com/paritytech/substrate/pull/8014)
-
-- Charge rent for code stored on the chain in addition to the already existing
-rent that is payed for data storage.
-[1](https://github.com/paritytech/substrate/pull/7935)
-
-- Allow the runtime to configure per storage item costs in addition
-to the already existing per byte costs.
-[1](https://github.com/paritytech/substrate/pull/7819)
-
-- Contracts are now deleted lazily so that the user who removes a contract
-does not need to pay for the deletion of the contract storage.
-[1](https://github.com/paritytech/substrate/pull/7740)
-
-- Allow runtime authors to define chain extensions in order to provide custom
-functionality to contracts.
-[1](https://github.com/paritytech/substrate/pull/7548)
-[2](https://github.com/paritytech/substrate/pull/8003)
-
-- Proper weights which are fully automated by benchmarking.
-[1](https://github.com/paritytech/substrate/pull/6715)
-[2](https://github.com/paritytech/substrate/pull/7017)
-[3](https://github.com/paritytech/substrate/pull/7361)
-
-### Changes
-
-- Collect the rent for one block during instantiation.
-[1](https://github.com/paritytech/substrate/pull/7847)
-
-- Instantiation takes a `salt` argument to allow for easier instantion of the
-same code by the same sender.
-[1](https://github.com/paritytech/substrate/pull/7482)
-
-- Improve the information returned by the `contracts_call` RPC.
-[1](https://github.com/paritytech/substrate/pull/7468)
-
-- Simplify the node configuration necessary to add this module.
-[1](https://github.com/paritytech/substrate/pull/7409)
-
-### Fixed
-
-- Consider the code size of a contract in the weight that is charged for
-loading a contract from storage.
-[1](https://github.com/paritytech/substrate/pull/8086)
-
-- Fix possible overflow in storage size calculation
-[1](https://github.com/paritytech/substrate/pull/7885)
-
-- Cap the surcharge reward that can be claimed.
-[1](https://github.com/paritytech/substrate/pull/7870)
-
-- Fix a possible DoS vector where contracts could allocate too large buffers.
-[1](https://github.com/paritytech/substrate/pull/7818)
pallets/contracts/COMPLEXITY.mddiffbeforeafterboth--- a/pallets/contracts/COMPLEXITY.md
+++ /dev/null
@@ -1,470 +0,0 @@
-# Complexity
-
-This analysis is on the computing and memory complexity of specific procedures. It provides a rough estimate of operations performed in general and especially focusing on DB reads and writes. It is also an attempt to estimate the memory consumption at its peak.
-
-The primary goal is to come up with decent pricing for functions that can be invoked by a user (via extrinsics) or by untrusted code that prevents DoS attacks.
-
-## Sandboxing
-
-It makes sense to describe the sandboxing module first because the smart-contract module is built upon it.
-
-### Memory
-
-#### set
-
-Copies data from the supervisor's memory to the guest's memory.
-
-**complexity**: It doesn't allocate, and the computational complexity is proportional to the number of bytes to copy.
-
-#### get
-
-Copies data from the guest's memory to the supervisor's memory.
-
-**complexity**: It doesn't allocate, and the computational complexity is proportional to the number of bytes to copy.
-
-## Instance
-
-### Instantiation
-
-Instantiation of a sandbox module consists of the following steps:
-
-1. Loading the wasm module in the in-memory representation,
-2. Performing validation of the wasm code,
-3. Setting up the environment which will be used to instantiate the module,
-4. Performing the standard wasm instantiation process, which includes (but is not limited to):
- 1. Allocating of memory requested by the instance,
- 2. Copying static data from the module to newly allocated memory,
- 3. Executing the `start` function.
-
-**Note** that the `start` function can be viewed as a normal function and can do anything that a normal function can do, including allocation of more memory or calling the host environment. The complexity of running the `start` function should be considered separately.
-
-In order to start the process of instantiation, the supervisor should provide the wasm module code being instantiated and the environment definition (a set of functions, memories (and maybe globals and tables in the future) available for import by the guest module) for that module. While the environment definition typically is of the constant size (unless mechanisms like dynamic linking are used), the size of wasm is not.
-
-Validation and instantiation in WebAssembly are designed to be able to be performed in linear time. The allocation and computational complexity of loading a wasm module depend on the underlying wasm VM being used. For example, for JIT compilers it can and probably will be non-linear because of compilation. However, for wasmi, it should be linear. We can try to use other VMs that are able to compile code with memory and time consumption proportional to the size of the code.
-
-Since the module itself requests memory, the amount of allocation depends on the module code itself. If untrusted code is being instantiated, it's up to the supervisor to limit the amount of memory available to allocate.
-
-**complexity**: The computational complexity is proportional to the size of wasm code. Memory complexity is proportional to the size of wasm code and the amount of memory requested by the module.
-
-### Preparation to invoke
-
-Invocation of an exported function in the sandboxed module consists of the following steps:
-
-1. Marshalling, copying and unmarshalling the arguments when passing them between the supervisor and executor,
-2. Calling into the underlying VM,
-3. Marshalling, copying and unmarshalling the result when passing it between the executor and supervisor,
-
-**Note** that the complexity of running the function code itself should be considered separately.
-
-The actual complexity of invocation depends on the underlying VM. Wasmi will reserve a relatively large chunk of memory for the stack before execution of the code, although it's of constant size.
-
-The size of the arguments and the return value depends on the exact function in question, but can be considered as constant.
-
-**complexity**: Memory and computational complexity can be considered as a constant.
-
-### Call from the guest to the supervisor
-
-The executor handles each call from the guest. The execution of it consists of the following steps:
-
-1. Marshalling, copying and unmarshalling the arguments when passing them between the guest and executor,
-2. Calling into the supervisor,
-3. Marshaling, copying and unmarshalling the result when passing it between the executor and guest.
-
-**Note** that the complexity of running the supervisor handler should be considered separately.
-
-Because calling into the supervisor requires invoking a wasm VM, the actual complexity of invocation depends on the actual VM used for the runtime/supervisor. Wasmi will reserve a relatively large chunk of memory for the stack before execution of the code, although it's of constant size.
-
-The size of the arguments and the return value depends on the exact function in question, but can be considered as a constant.
-
-**complexity**: Memory and computational complexity can be considered as a constant.
-
-## Transactional Storage
-
-The contracts module makes use of the nested storage transactions feature offered by
-the underlying storage which allows efficient roll back of changes made by contracts.
-
-> ℹ️ The underlying storage has a overlay layer implemented as a `Map`. If the runtime reads a storage location and the
-> respective key doesn't exist in the overlay, then the underlying storage performs a DB access, but the value won't be
-> placed into the overlay. The overlay is only filled with writes.
->
-> This means that the overlay can be abused in the following ways:
->
-> - The overlay can be inflated by issuing a lot of writes to unique locations,
-> - Deliberate cache misses can be induced by reading non-modified storage locations,
-
-It also worth noting that the performance degrades with more state stored in the trie. Due to this
-there is not negligible chance that gas schedule will be updated for all operations that involve
-storage access.
-
-## get_storage, get_code_hash, get_rent_allowance, get_balance, contract_exists
-
-Those query the underlying storage for the requested value. If the value was modified in the
-current block they are served from the cache. Otherwise a database read is performed.
-
-**complexity**: The memory complexity is proportional to the size of the value. The computational complexity is proportional the size of the value; the cost is dominated by the DB read.
-
-## set_storage, set_balance, set_rent_allowance
-
-These function write to the underlying storage which caches those values and does not write
-them to the database immediately.
-
-While these functions only modify the local cache, they trigger a database write later when
-all changes that were not rolled back are written to storage. Moreover, if the balance of the
-account is changed to be below `existential_deposit` then that account along with all its storage
-will be removed, which requires time proportional to the number of storage entries that account has.
-It should be ensured that pricing accounts for these facts.
-
-**complexity**: Each lookup has a logarithmical computing time to the number of already inserted entries.
-No additional memory is required.
-
-## instantiate_contract
-
-Calls `contract_exists` and if it doesn't exist, do not modify the local `Map` similarly to `set_rent_allowance`.
-
-**complexity**: The computational complexity is proportional to the depth of the overlay cascade and the size of the value; the cost is dominated by the DB read though. No additional memory is required.
-
-## commit
-
-In this function, all values modified in the current transactions are committed to the parent
-transaction.
-
-This will trigger `N` inserts into parent transaction (`O(log M)` complexity) or into the storage, where `N` is the size of the current transaction and `M` is the size of the parent transaction. Consider adjusting the price of modifying the
-current transaction to account for this (since pricing for the count of entries in `commit` will make the price of commit way less predictable). No additional memory is required.
-
-Note that in case of storage modification we need to construct a key in the underlying storage. In order to do that we need:
-
-- perform `twox_128` hashing over a concatenation of some prefix literal and the `AccountId` of the storage owner.
-- then perform `blake2_256` hashing of the storage key.
-- concatenation of these hashes will constitute the key in the underlying storage.
-
-There is also a special case to think of: if the balance of some account goes below `existential_deposit`, then all storage entries of that account will be erased, which requires time proportional to the number of storage entries that account has.
-
-**complexity**: `N` inserts into a transaction or eventually into the storage (if committed). Every deleted account will induce removal of all its storage which is proportional to the number of storage entries that account has.
-
-## revert
-
-Consists of dropping (in the Rust sense) of the current transaction.
-
-**complexity**: Computing complexity is proportional to a number of changed entries in a overlay. No additional memory is required.
-
-## Executive
-
-### Transfer
-
-This function performs the following steps:
-
-1. Querying source and destination balances from the current transaction (see `get_balance`),
-2. Querying `existential_deposit`.
-3. Executing `ensure_account_liquid` hook.
-4. Updating source and destination balance in the overlay (see `set_balance`).
-
-**Note** that the complexity of executing `ensure_account_liquid` hook should be considered separately.
-
-In the course of the execution this function can perform up to 2 DB reads to `get_balance` of source and destination accounts. It can also induce up to 2 DB writes via `set_balance` if flushed to the storage.
-
-Moreover, if the source balance goes below `existential_deposit` then the transfer is denied and
-returns with an error.
-
-Assuming marshaled size of a balance value is of the constant size we can neglect its effect on the performance.
-
-**complexity**: up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has. Memorywise it can be assumed to be constant.
-
-### Initialization
-
-Before a call or instantiate can be performed the execution context must be initialized.
-
-For the first call or instantiation in the handling of an extrinsic, this involves two calls:
-
-1. `<timestamp::Module<T>>::now()`
-2. `<system::Pallet<T>>::block_number()`
-
-The complexity of initialization depends on the complexity of these functions. In the current
-implementation they just involve a DB read.
-
-For subsequent calls and instantiations during contract execution, the initialization requires no
-expensive operations.
-
-### Terminate
-
-This function performs the following steps:
-
-1. Check the calling contract is not already on the callstack by calling `is_live`.
-2. `transfer` funds from caller to the beneficiary.
-3. Flag the caller contract as deleted in the overlay.
-
-`is_live` does not do any database access nor does it allocate memory. It walks up the call
-stack and therefore executes in linear time depending on size of the call stack. Because
-the call stack is of a fixed maximum size we consider this operation as constant time.
-
-**complexity**: Database accesses as described in Transfer + Removal of the contract. Currently,
-we are using child trie removal which is linear in the amount of stored keys. Upcoming changes
-will make the account removal constant time.
-
-### Call
-
-This function receives input data for the contract execution. The execution consists of the following steps:
-
-1. Initialization of the execution context.
-2. Checking rent payment.
-3. Loading code from the DB.
-4. Starting a new storage transaction.
-5. `transfer`-ing funds between the caller and the destination account.
-6. Executing the code of the destination account.
-7. Committing or rolling back the storage transaction.
-
-**Note** that the complexity of executing the contract code should be considered separately.
-
-Checking for rent involves 2 unconditional DB reads: `ContractInfoOf` and `block_number`
-and on top of that at most once per block:
-
-- DB read to `free_balance` and
-- `rent_deposit_offset` and
-- `rent_byte_price` and
-- `Currency::minimum_balance` and
-- `tombstone_deposit`.
-- Calls to `ensure_can_withdraw`, `withdraw`, `make_free_balance_be` can perform arbitrary logic and should be considered separately,
-- `child_storage_root`
-- `kill_child_storage`
-- mutation of `ContractInfoOf`
-
-Loading code most likely will trigger a DB read, since the code is immutable and therefore will not get into the cache (unless a suicide removes it, or it has been instantiated in the same call chain).
-
-Also, `transfer` can make up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has.
-
-Finally, the current storage transaction is closed. The complexity of this depends on the number of changes performed by the code. Thus, the pricing of storage modification should account for that.
-
-**complexity**:
-
-- Only for the first invocation of the contract: up to 5 DB reads and one DB write as well as logic executed by `ensure_can_withdraw`, `withdraw`, `make_free_balance_be`.
-- On top of that for every invocation: Up to 5 DB reads. DB read of the code is of dynamic size. There can also be up to 2 DB writes (if flushed to the storage). Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has.
-
-### Instantiate
-
-This function takes the code of the constructor and input data. Instantiation of a contract consists of the following steps:
-
-1. Initialization of the execution context.
-2. Calling `DetermineContractAddress` hook to determine an address for the contract,
-3. Starting a new storage transaction.
-4. `transfer`-ing funds between self and the newly instantiated contract.
-5. Executing the constructor code. This will yield the final code of the code.
-6. Storing the code for the newly instantiated contract in the overlay.
-7. Committing or rolling back the storage transaction.
-
-**Note** that the complexity of executing the constructor code should be considered separately.
-
-**Note** that the complexity of `DetermineContractAddress` hook should be considered separately as well. Most likely it will use some kind of hashing over the code of the constructor and input data. The default `SimpleAddressDeterminer` does precisely that.
-
-**Note** that the constructor returns code in the owned form and it's obtained via return facilities, which should have take fee for the return value.
-
-Also, `transfer` can make up to 2 DB reads and up to 2 DB writes (if flushed to the storage) in the standard case. If removal of the source account takes place then it will additionally perform a DB write per one storage entry that the account has.
-
-Storing the code in the overlay may induce another DB write (if flushed to the storage) with the size proportional to the size of the constructor code.
-
-Finally, the current storage transaction is closed.. The complexity of this depends on the number of changes performed by the constructor code. Thus, the pricing of storage modification should account for that.
-
-**complexity**: Up to 2 DB reads and induces up to 3 DB writes (if flushed to the storage), one of which is dependent on the size of the code. Additionally, if the source account removal takes place a DB write will be performed per one storage entry that the account has.
-
-## Contracts API
-
-Each API function invoked from a contract can involve some overhead.
-
-## Getter functions
-
-Those are simple getter functions which copy a requested value to contract memory. They
-all have the following two arguments:
-
-- `output_ptr`: Pointer into contract memory where to copy the value.
-- `output_len_ptr`: Pointer into contract memory where the size of the buffer is stored. The size of the copied value is also stored there.
-
-**complexity**: The size of the returned value is constant for a given runtime. Therefore we
-consider its complexity constant even though some of them might involve at most one DB read. Some of those
-functions call into other pallets of the runtime. The assumption here is that those functions are also
-linear in regard to the size of the data that is returned and therefore considered constant for a
-given runtime.
-
-This is the list of getters:
-
-- seal_caller
-- seal_address
-- seal_weight_to_fee
-- seal_gas_left
-- seal_balance
-- seal_value_transferred
-- seal_now
-- seal_minimum_balance
-- seal_tombstone_deposit
-- seal_rent_allowance
-- seal_block_number
-
-### seal_set_storage
-
-This function receives a `key` and `value` as arguments. It consists of the following steps:
-
-1. Reading the sandbox memory for `key` and `value` (see sandboxing memory get).
-2. Setting the storage at the given `key` to the given `value` (see `set_storage`).
-
-**complexity**: Complexity is proportional to the size of the `value`. This function induces a DB write of size proportional to the `value` size (if flushed to the storage), so should be priced accordingly.
-
-### seal_clear_storage
-
-This function receives a `key` as argument. It consists of the following steps:
-
-1. Reading the sandbox memory for `key` (see sandboxing memory get).
-2. Clearing the storage at the given `key` (see `set_storage`).
-
-**complexity**: Complexity is constant. This function induces a DB write to clear the storage entry
-(upon being flushed to the storage) and should be priced accordingly.
-
-### seal_get_storage
-
-This function receives a `key` as an argument. It consists of the following steps:
-
-1. Reading the sandbox memory for `key` (see sandboxing memory get).
-2. Reading the storage with the given key (see `get_storage`). It receives back the owned result buffer.
-3. Writing the storage value to contract memory.
-
-Key is of a constant size. Therefore, the sandbox memory load can be considered to be of constant complexity.
-
-Unless the value is cached, a DB read will be performed. The size of the value is not known until the read is
-performed. Moreover, the DB read has to be synchronous and no progress can be made until the value is fetched.
-
-**complexity**: The memory and computing complexity is proportional to the size of the fetched value. This function performs a DB read.
-
-### seal_transfer
-
-This function receives the following arguments:
-
-- `account` buffer of a marshaled `AccountId`,
-- `value` buffer of a marshaled `Balance`,
-
-It consists of the following steps:
-
-1. Loading `account` buffer from the sandbox memory (see sandboxing memory get) and then decoding it.
-2. Loading `value` buffer from the sandbox memory and then decoding it.
-3. Invoking the executive function `transfer`.
-
-Loading of `account` and `value` buffers should be charged. This is because the sizes of buffers are specified by the calling code, even though marshaled representations are, essentially, of constant size. This can be fixed by assigning an upper bound for sizes of `AccountId` and `Balance`.
-
-### seal_call
-
-This function receives the following arguments:
-
-- `callee` buffer of a marshaled `AccountId`,
-- `gas` limit which is plain u64,
-- `value` buffer of a marshaled `Balance`,
-- `input_data` an arbitrarily sized byte vector.
-- `output_ptr` pointer to contract memory.
-
-It consists of the following steps:
-
-1. Loading `callee` buffer from the sandbox memory (see sandboxing memory get) and then decoding it.
-2. Loading `value` buffer from the sandbox memory and then decoding it.
-3. Loading `input_data` buffer from the sandbox memory.
-4. Invoking the executive function `call`.
-5. Writing output buffer to contract memory.
-
-Loading of `callee` and `value` buffers should be charged. This is because the sizes of buffers are specified by the calling code, even though marshaled representations are, essentially, of constant size. This can be fixed by assigning an upper bound for sizes of `AccountId` and `Balance`.
-
-Loading `input_data` should be charged in any case.
-
-**complexity**: All complexity comes from loading and writing buffers and executing `call` executive function. The former component is proportional to the sizes of `callee`, `value`, `input_data` and `output_ptr` buffers. The latter component completely depends on the complexity of `call` executive function, and also dominated by it.
-
-### seal_instantiate
-
-This function receives the following arguments:
-
-- `init_code`, a buffer which contains the code of the constructor.
-- `gas` limit which is plain u64
-- `value` buffer of a marshaled `Balance`
-- `input_data`. an arbitrarily sized byte vector.
-
-It consists of the following steps:
-
-1. Loading `init_code` buffer from the sandbox memory (see sandboxing memory get) and then decoding it.
-2. Loading `value` buffer from the sandbox memory and then decoding it.
-3. Loading `input_data` buffer from the sandbox memory.
-4. Invoking `instantiate` executive function.
-
-Loading of `value` buffer should be charged. This is because the size of the buffer is specified by the calling code, even though marshaled representation is, essentially, of constant size. This can be fixed by assigning an upper bound for size for `Balance`.
-
-Loading `init_code` and `input_data` should be charged in any case.
-
-**complexity**: All complexity comes from loading buffers and executing `instantiate` executive function. The former component is proportional to the sizes of `init_code`, `value` and `input_data` buffers. The latter component completely depends on the complexity of `instantiate` executive function and also dominated by it.
-
-### seal_terminate
-
-This function receives the following arguments:
-
-- `beneficiary`, buffer of a marshaled `AccountId`
-
-It consists of the following steps:
-
-1. Loading `beneficiary` buffer from the sandbox memory (see sandboxing memory get) and then decoding it.
-
-Loading of the `beneficiary` buffer should be charged. This is because the sizes of buffers are specified by the calling code, even though marshaled representations are, essentially, of constant size. This can be fixed by assigning an upper bound for sizes of `AccountId`.
-
-**complexity**: All complexity comes from loading buffers and executing `terminate` executive function. The former component is proportional to the size of the `beneficiary` buffer. The latter component completely depends on the complexity of `terminate` executive function and also dominated by it.
-
-### seal_input
-
-This function receives a pointer to contract memory. It copies the input to the contract call to this location.
-
-**complexity**: The complextity is proportional to the size of the input buffer.
-
-### seal_return
-
-This function receives a `data` buffer and `flags` arguments. Execution of the function consists of the following steps:
-
-1. Loading `data` buffer from the sandbox memory (see sandboxing memory get).
-2. Storing the `u32` flags value.
-3. Trapping
-
-**complexity**: The complexity of this function is proportional to the size of the `data` buffer.
-
-### seal_deposit_event
-
-This function receives a `data` buffer as an argument. Execution of the function consists of the following steps:
-
-1. Loading `data` buffer from the sandbox memory (see sandboxing memory get),
-2. Insert to nested context execution
-3. Copies from nested to underlying contexts
-4. Call system deposit event
-
-**complexity**: The complexity of this function is proportional to the size of the `data` buffer.
-
-### seal_set_rent_allowance
-
-This function receives the following argument:
-
-- `value` buffer of a marshaled `Balance`,
-
-It consists of the following steps:
-
-1. Loading `value` buffer from the sandbox memory and then decoding it.
-2. Invoking `set_rent_allowance` AccountDB function.
-
-**complexity**: Complexity is proportional to the size of the `value`. This function induces a DB write of size proportional to the `value` size (if flushed to the storage), so should be priced accordingly.
-
-## Built-in hashing functions
-
-This paragraph concerns the following supported built-in hash functions:
-
-- `SHA2` with 256-bit width
-- `KECCAK` with 256-bit width
-- `BLAKE2` with 128-bit and 256-bit widths
-
-These functions compute a cryptographic hash on the given inputs and copy the
-resulting hash directly back into the sandboxed Wasm contract output buffer.
-
-Execution of the function consists of the following steps:
-
-1. Load data stored in the input buffer into an intermediate buffer.
-2. Compute the cryptographic hash `H` on the intermediate buffer.
-3. Copy back the bytes of `H` into the contract side output buffer.
-
-**complexity**: Complexity is proportional to the size of the input buffer in bytes
-as well as to the size of the output buffer in bytes. Also different cryptographic
-algorithms have different inherent complexity so users must expect the above
-mentioned crypto hashes to have varying gas costs.
-The complexity of each cryptographic hash function highly depends on the underlying
-implementation.
pallets/contracts/Cargo.tomldiffbeforeafterboth--- a/pallets/contracts/Cargo.toml
+++ /dev/null
@@ -1,75 +0,0 @@
-[package]
-name = "pallet-contracts"
-version = "3.0.0"
-authors = ["Parity Technologies <admin@parity.io>"]
-edition = "2018"
-license = "Apache-2.0"
-homepage = "https://substrate.dev"
-repository = "https://github.com/paritytech/substrate/"
-description = "FRAME pallet for WASM contracts"
-readme = "README.md"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[dependencies]
-codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
-log = { version = "0.4", default-features = false }
-parity-wasm = { version = "0.42", default-features = false }
-pwasm-utils = { version = "0.17", default-features = false }
-serde = { version = "1", optional = true, features = ["derive"] }
-wasmi-validation = { version = "0.4", default-features = false }
-
-# Only used in benchmarking to generate random contract code
-rand = { version = "0.8", optional = true, default-features = false }
-rand_pcg = { version = "0.3", optional = true }
-
-# Substrate Dependencies
-frame-benchmarking = { version = "3.1.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3', optional = true }
-frame-support = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-frame-system = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts-primitives = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts-proc-macro = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-core = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-io = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-sandbox = { version = "0.9.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-std = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[dev-dependencies]
-assert_matches = "1"
-hex-literal = "0.3"
-paste = "1"
-pretty_assertions = "0.7"
-wat = "1"
-
-# Substrate Dependencies
-pallet-balances = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-timestamp = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-randomness-collective-flip = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[features]
-default = ["std"]
-std = [
- "serde",
- "codec/std",
- "sp-core/std",
- "sp-runtime/std",
- "sp-io/std",
- "sp-std/std",
- "sp-sandbox/std",
- "frame-support/std",
- "frame-system/std",
- "parity-wasm/std",
- "pwasm-utils/std",
- "wasmi-validation/std",
- "pallet-contracts-primitives/std",
- "pallet-contracts-proc-macro/full",
- "log/std",
-]
-runtime-benchmarks = [
- "frame-benchmarking",
- "rand",
- "rand_pcg",
-]
-
pallets/contracts/README.mddiffbeforeafterboth--- a/pallets/contracts/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Contract Module
-
-The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.
-
-- [`Call`](https://docs.rs/pallet-contracts/latest/pallet_contracts/enum.Call.html)
-- [`Config`](https://docs.rs/pallet-contracts/latest/pallet_contracts/trait.Config.html)
-- [`Error`](https://docs.rs/pallet-contracts/latest/pallet_contracts/enum.Error.html)
-- [`Event`](https://docs.rs/pallet-contracts/latest/pallet_contracts/enum.Event.html)
-
-## Overview
-
-This module extends accounts based on the `Currency` trait to have smart-contract functionality. It can
-be used with other modules that implement accounts based on `Currency`. 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 in a `code_cache`, and later retrievable via its `code_hash`.
-This means that multiple smart-contracts can be instantiated from the same `code_cache`, 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.
-
-### Gas
-
-Senders must specify a gas limit with every call, as all instructions invoked by the smart-contract require gas.
-Unused gas is refunded after the call, regardless of the execution outcome.
-
-If the gas 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.
-
-One gas is equivalent to one [weight](https://substrate.dev/docs/en/knowledgebase/learn-substrate/weight)
-which is defined as one picosecond of execution time on the runtime's reference machine.
-
-### 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
-
-Those are documented in the [reference documentation](https://docs.rs/pallet-contracts/latest/pallet_contracts/#dispatchable-functions).
-
-## Usage
-
-This module executes WebAssembly smart contracts. These can potentially be written in any language
-that compiles to web assembly. However, using a language that specifically targets this module
-will make things a lot easier. One such language is [`ink`](https://github.com/paritytech/ink)
-which is an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables
-writing WebAssembly based smart contracts in the Rust programming language.
-
-## Debugging
-
-Contracts can emit messages to the node console when run on a development chain through the
-`seal_println` API. This is exposed in ink! via
-[`ink_env::debug_println()`](https://docs.rs/ink_env/latest/ink_env/fn.debug_println.html).
-
-In order to see these messages the log level for the `runtime::contracts` target needs to be raised
-to at least the `info` level which is the default. However, those messages are easy to overlook
-because of the noise generated by block production. A good starting point for contract debugging
-could be:
-
-```bash
-cargo run --release -- --dev --tmp -lerror,runtime::contracts
-```
-
-License: Apache-2.0
pallets/contracts/common/Cargo.tomldiffbeforeafterboth--- a/pallets/contracts/common/Cargo.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[package]
-name = "pallet-contracts-primitives"
-version = "3.0.0"
-authors = ["Parity Technologies <admin@parity.io>"]
-edition = "2018"
-license = "Apache-2.0"
-homepage = "https://substrate.dev"
-repository = "https://github.com/paritytech/substrate/"
-description = "A crate that hosts a common definitions that are relevant for the pallet-contracts."
-readme = "README.md"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[dependencies]
-bitflags = "1.0"
-codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
-serde = { version = "1", features = ["derive"], optional = true }
-
-# Substrate Dependencies (This crate should not rely on frame)
-sp-core = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3', default-features = false }
-sp-std = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[features]
-default = ["std"]
-std = [
- "codec/std",
- "sp-core/std",
- "sp-runtime/std",
- "sp-std/std",
- "serde",
-]
pallets/contracts/common/README.mddiffbeforeafterboth--- a/pallets/contracts/common/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-A crate that hosts a common definitions that are relevant for the pallet-contracts.
-
-License: Apache-2.0
\ No newline at end of file
pallets/contracts/common/src/lib.rsdiffbeforeafterboth--- a/pallets/contracts/common/src/lib.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-//! A crate that hosts a common definitions that are relevant for the pallet-contracts.
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-use bitflags::bitflags;
-use codec::{Decode, Encode};
-use sp_core::Bytes;
-use sp_runtime::{DispatchError, RuntimeDebug};
-use sp_std::prelude::*;
-
-#[cfg(feature = "std")]
-use serde::{Serialize, Deserialize};
-
-/// Result type of a `bare_call` or `bare_instantiate` call.
-///
-/// It contains the execution result together with some auxiliary information.
-#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
-pub struct ContractResult<T> {
- /// How much gas was consumed during execution.
- pub gas_consumed: u64,
- /// An optional debug message. This message is only non-empty when explicitly requested
- /// by the code that calls into the contract.
- ///
- /// The contained bytes are valid UTF-8. This is not declared as `String` because
- /// this type is not allowed within the runtime. A client should decode them in order
- /// to present the message to its users.
- ///
- /// # Note
- ///
- /// The debug message is never generated during on-chain execution. It is reserved for
- /// RPC calls.
- pub debug_message: Bytes,
- /// The execution result of the wasm code.
- pub result: T,
-}
-
-/// Result type of a `bare_call` call.
-pub type ContractExecResult = ContractResult<Result<ExecReturnValue, DispatchError>>;
-
-/// Result type of a `bare_instantiate` call.
-pub type ContractInstantiateResult<AccountId, BlockNumber> =
- ContractResult<Result<InstantiateReturnValue<AccountId, BlockNumber>, DispatchError>>;
-
-/// Result type of a `get_storage` call.
-pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
-
-/// Result type of a `rent_projection` call.
-pub type RentProjectionResult<BlockNumber> =
- Result<RentProjection<BlockNumber>, ContractAccessError>;
-
-/// The possible errors that can happen querying the storage of a contract.
-#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
-pub enum ContractAccessError {
- /// The given address doesn't point to a contract.
- DoesntExist,
- /// The specified contract is a tombstone and thus cannot have any storage.
- IsTombstone,
-}
-
-#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
-pub enum RentProjection<BlockNumber> {
- /// Eviction is projected to happen at the specified block number.
- EvictionAt(BlockNumber),
- /// No eviction is scheduled.
- ///
- /// E.g. Contract accumulated enough funds to offset the rent storage costs.
- NoEviction,
-}
-
-bitflags! {
- /// Flags used by a contract to customize exit behaviour.
- #[derive(Encode, Decode)]
- #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
- #[cfg_attr(feature = "std", serde(rename_all = "camelCase", transparent))]
- pub struct ReturnFlags: u32 {
- /// If this bit is set all changes made by the contract execution are rolled back.
- const REVERT = 0x0000_0001;
- }
-}
-
-/// Output of a contract call or instantiation which ran to completion.
-#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
-pub struct ExecReturnValue {
- /// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
- pub flags: ReturnFlags,
- /// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
- pub data: Bytes,
-}
-
-impl ExecReturnValue {
- /// We understand the absense of a revert flag as success.
- pub fn is_success(&self) -> bool {
- !self.flags.contains(ReturnFlags::REVERT)
- }
-}
-
-/// The result of a successful contract instantiation.
-#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
-pub struct InstantiateReturnValue<AccountId, BlockNumber> {
- /// The output of the called constructor.
- pub result: ExecReturnValue,
- /// The account id of the new contract.
- pub account_id: AccountId,
- /// Information about when and if the new project will be evicted.
- ///
- /// # Note
- ///
- /// `None` if `bare_instantiate` was called with
- /// `compute_projection` set to false. From the perspective of an RPC this means that
- /// the runtime API did not request this value and this feature is therefore unsupported.
- pub rent_projection: Option<RentProjection<BlockNumber>>,
-}
-
-/// Reference to an existing code hash or a new wasm module.
-#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
-pub enum Code<Hash> {
- /// A wasm module as raw bytes.
- Upload(Bytes),
- /// The code hash of an on-chain wasm blob.
- Existing(Hash),
-}
pallets/contracts/fixtures/call_return_code.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/call_return_code.wat
+++ /dev/null
@@ -1,42 +0,0 @@
-;; This calls the supplied dest and transfers 100 balance during this call and copies
-;; the return code of this call to the output buffer.
-;; It also forwards its input to the callee.
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 8) 100 balance
- (data (i32.const 0) "\64\00\00\00\00\00\00\00")
-
- ;; [8, 12) here we store the return code of the transfer
-
- ;; [12, 16) size of the input data
- (data (i32.const 12) "\24")
-
- ;; [16, inf) here we store the input data
- ;; 32 byte dest + 4 byte forward
-
- (func (export "deploy"))
-
- (func (export "call")
- (call $seal_input (i32.const 16) (i32.const 12))
- (i32.store
- (i32.const 8)
- (call $seal_call
- (i32.const 16) ;; Pointer to "callee" address.
- (i32.const 32) ;; Length of "callee" address.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 48) ;; Pointer to input data buffer address
- (i32.const 4) ;; Length of input data buffer
- (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Ptr to output buffer len
- )
- )
- ;; exit with success and take transfer return code to the output buffer
- (call $seal_return (i32.const 0) (i32.const 8) (i32.const 4))
- )
-)
pallets/contracts/fixtures/caller_contract.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/caller_contract.wat
+++ /dev/null
@@ -1,293 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_balance" (func $seal_balance (param i32 i32)))
- (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_instantiate" (func $seal_instantiate
- (param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)
- ))
- (import "seal0" "seal_println" (func $seal_println (param i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func $current_balance (param $sp i32) (result i64)
- (i32.store
- (i32.sub (get_local $sp) (i32.const 16))
- (i32.const 8)
- )
- (call $seal_balance
- (i32.sub (get_local $sp) (i32.const 8))
- (i32.sub (get_local $sp) (i32.const 16))
- )
- (call $assert
- (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 16))) (i32.const 8))
- )
- (i64.load (i32.sub (get_local $sp) (i32.const 8)))
- )
-
- (func (export "deploy"))
-
- (func (export "call")
- (local $sp i32)
- (local $exit_code i32)
- (local $balance i64)
-
- ;; Length of the buffer
- (i32.store (i32.const 20) (i32.const 32))
-
- ;; Copy input to this contracts memory
- (call $seal_input (i32.const 24) (i32.const 20))
-
- ;; Input data is the code hash of the contract to be deployed.
- (call $assert
- (i32.eq
- (i32.load (i32.const 20))
- (i32.const 32)
- )
- )
-
- ;; Read current balance into local variable.
- (set_local $sp (i32.const 1024))
- (set_local $balance
- (call $current_balance (get_local $sp))
- )
-
- ;; Fail to deploy the contract since it returns a non-zero exit status.
- (set_local $exit_code
- (call $seal_instantiate
- (i32.const 24) ;; Pointer to the code hash.
- (i32.const 32) ;; Length of the code hash.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 9) ;; Pointer to input data buffer address
- (i32.const 7) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy address
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 0) ;; salt_ptr
- (i32.const 0) ;; salt_le
- )
- )
-
- ;; Check non-zero exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 2)) ;; ReturnCode::CalleeReverted
- )
-
- ;; Check that balance has not changed.
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
-
- ;; Fail to deploy the contract due to insufficient gas.
- (set_local $exit_code
- (call $seal_instantiate
- (i32.const 24) ;; Pointer to the code hash.
- (i32.const 32) ;; Length of the code hash.
- (i64.const 1) ;; Supply too little gas
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 8) ;; Pointer to input data buffer address
- (i32.const 8) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy address
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 0) ;; salt_ptr
- (i32.const 0) ;; salt_le
-
- )
- )
-
- ;; Check for special trap exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped
- )
-
- ;; Check that balance has not changed.
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
-
- ;; Length of the output buffer
- (i32.store
- (i32.sub (get_local $sp) (i32.const 4))
- (i32.const 256)
- )
-
- ;; Deploy the contract successfully.
- (set_local $exit_code
- (call $seal_instantiate
- (i32.const 24) ;; Pointer to the code hash.
- (i32.const 32) ;; Length of the code hash.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 8) ;; Pointer to input data buffer address
- (i32.const 8) ;; Length of input data buffer
- (i32.const 16) ;; Pointer to the address output buffer
- (i32.sub (get_local $sp) (i32.const 4)) ;; Pointer to the address buffer length
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 0) ;; salt_ptr
- (i32.const 0) ;; salt_le
-
- )
- )
-
- ;; Check for success exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 0)) ;; ReturnCode::Success
- )
-
- ;; Check that address has the expected length
- (call $assert
- (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 4))) (i32.const 32))
- )
-
- ;; Check that balance has been deducted.
- (set_local $balance
- (i64.sub (get_local $balance) (i64.load (i32.const 0)))
- )
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
-
- ;; Zero out destination buffer of output
- (i32.store
- (i32.sub (get_local $sp) (i32.const 4))
- (i32.const 0)
- )
-
- ;; Length of the output buffer
- (i32.store
- (i32.sub (get_local $sp) (i32.const 8))
- (i32.const 4)
- )
-
- ;; Call the new contract and expect it to return failing exit code.
- (set_local $exit_code
- (call $seal_call
- (i32.const 16) ;; Pointer to "callee" address.
- (i32.const 32) ;; Length of "callee" address.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 9) ;; Pointer to input data buffer address
- (i32.const 7) ;; Length of input data buffer
- (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer
- (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len
- )
- )
-
- ;; Check non-zero exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 2)) ;; ReturnCode::CalleeReverted
- )
-
- ;; Check that output buffer contains the expected return data.
- (call $assert
- (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 8))) (i32.const 3))
- )
- (call $assert
- (i32.eq
- (i32.load (i32.sub (get_local $sp) (i32.const 4)))
- (i32.const 0x00776655)
- )
- )
-
- ;; Check that balance has not changed.
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
-
- ;; Fail to call the contract due to insufficient gas.
- (set_local $exit_code
- (call $seal_call
- (i32.const 16) ;; Pointer to "callee" address.
- (i32.const 32) ;; Length of "callee" address.
- (i64.const 1) ;; Supply too little gas
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 8) ;; Pointer to input data buffer address
- (i32.const 8) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this cas
- )
- )
-
- ;; Check for special trap exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 1)) ;; ReturnCode::CalleeTrapped
- )
-
- ;; Check that balance has not changed.
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
-
- ;; Zero out destination buffer of output
- (i32.store
- (i32.sub (get_local $sp) (i32.const 4))
- (i32.const 0)
- )
-
- ;; Length of the output buffer
- (i32.store
- (i32.sub (get_local $sp) (i32.const 8))
- (i32.const 4)
- )
-
- ;; Call the contract successfully.
- (set_local $exit_code
- (call $seal_call
- (i32.const 16) ;; Pointer to "callee" address.
- (i32.const 32) ;; Length of "callee" address.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 8) ;; Pointer to input data buffer address
- (i32.const 8) ;; Length of input data buffer
- (i32.sub (get_local $sp) (i32.const 4)) ;; Ptr to output buffer
- (i32.sub (get_local $sp) (i32.const 8)) ;; Ptr to output buffer len
- )
- )
-
- ;; Check for success exit status.
- (call $assert
- (i32.eq (get_local $exit_code) (i32.const 0)) ;; ReturnCode::Success
- )
-
- ;; Check that the output buffer contains the expected return data.
- (call $assert
- (i32.eq (i32.load (i32.sub (get_local $sp) (i32.const 8))) (i32.const 4))
- )
- (call $assert
- (i32.eq
- (i32.load (i32.sub (get_local $sp) (i32.const 4)))
- (i32.const 0x77665544)
- )
- )
-
- ;; Check that balance has been deducted.
- (set_local $balance
- (i64.sub (get_local $balance) (i64.load (i32.const 0)))
- )
- (call $assert
- (i64.eq (get_local $balance) (call $current_balance (get_local $sp)))
- )
- )
-
- (data (i32.const 0) "\00\80") ;; The value to transfer on instantiation and calls.
- ;; Chosen to be greater than existential deposit.
- (data (i32.const 8) "\00\01\22\33\44\55\66\77") ;; The input data to instantiations and calls.
-)
pallets/contracts/fixtures/chain_extension.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/chain_extension.wat
+++ /dev/null
@@ -1,46 +0,0 @@
-;; Call chain extension by passing through input and output of this contract
-(module
- (import "seal0" "seal_call_chain_extension"
- (func $seal_call_chain_extension (param i32 i32 i32 i32 i32) (result i32))
- )
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 16 16))
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok (get_local 0))
- (unreachable)
- )
- )
-
- ;; [0, 4) len of input output
- (data (i32.const 0) "\02")
-
- ;; [4, 12) buffer for input
-
- ;; [12, 16) len of output buffer
- (data (i32.const 12) "\02")
-
- ;; [16, inf) buffer for output
-
- (func (export "deploy"))
-
- (func (export "call")
- (call $seal_input (i32.const 4) (i32.const 0))
-
- ;; the chain extension passes through the input and returns it as output
- (call $seal_call_chain_extension
- (i32.load8_u (i32.const 4)) ;; func_id
- (i32.const 4) ;; input_ptr
- (i32.load (i32.const 0)) ;; input_len
- (i32.const 16) ;; output_ptr
- (i32.const 12) ;; output_len_ptr
- )
-
- ;; the chain extension passes through the func_id
- (call $assert (i32.eq (i32.load8_u (i32.const 4))))
-
- (call $seal_return (i32.const 0) (i32.const 16) (i32.load (i32.const 12)))
- )
-)
pallets/contracts/fixtures/check_default_rent_allowance.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/check_default_rent_allowance.wat
+++ /dev/null
@@ -1,43 +0,0 @@
-(module
- (import "seal0" "seal_rent_allowance" (func $seal_rent_allowance (param i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 8) reserved for $seal_rent_allowance output
-
- ;; [8, 16) length of the buffer
- (data (i32.const 8) "\08")
-
- ;; [16, inf) zero initialized
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "call"))
-
- (func (export "deploy")
- ;; fill the buffer with the rent allowance.
- (call $seal_rent_allowance (i32.const 0) (i32.const 8))
-
- ;; assert len == 8
- (call $assert
- (i32.eq
- (i32.load (i32.const 8))
- (i32.const 8)
- )
- )
-
- ;; assert that contents of the buffer is equal to <BalanceOf<T>>::max_value().
- (call $assert
- (i64.eq
- (i64.load (i32.const 0))
- (i64.const 0xFFFFFFFFFFFFFFFF)
- )
- )
- )
-)
pallets/contracts/fixtures/crypto_hashes.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/crypto_hashes.wat
+++ /dev/null
@@ -1,81 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
-
- (import "seal0" "seal_hash_sha2_256" (func $seal_hash_sha2_256 (param i32 i32 i32)))
- (import "seal0" "seal_hash_keccak_256" (func $seal_hash_keccak_256 (param i32 i32 i32)))
- (import "seal0" "seal_hash_blake2_256" (func $seal_hash_blake2_256 (param i32 i32 i32)))
- (import "seal0" "seal_hash_blake2_128" (func $seal_hash_blake2_128 (param i32 i32 i32)))
-
- (import "env" "memory" (memory 1 1))
-
- (type $hash_fn_sig (func (param i32 i32 i32)))
- (table 8 funcref)
- (elem (i32.const 1)
- $seal_hash_sha2_256
- $seal_hash_keccak_256
- $seal_hash_blake2_256
- $seal_hash_blake2_128
- )
- (data (i32.const 1) "20202010201008") ;; Output sizes of the hashes in order in hex.
-
- ;; Not in use by the tests besides instantiating the contract.
- (func (export "deploy"))
-
- ;; Called by the tests.
- ;;
- ;; The `call` function expects data in a certain format in the input buffer.
- ;;
- ;; 1. The first byte encodes an identifier for the crypto hash function
- ;; under test. (*)
- ;; 2. The rest encodes the input data that is directly fed into the
- ;; crypto hash function chosen in 1.
- ;;
- ;; The `deploy` function then computes the chosen crypto hash function
- ;; given the input and puts the result into the output buffer.
- ;; After contract execution the test driver then asserts that the returned
- ;; values are equal to the expected bytes for the input and chosen hash
- ;; function.
- ;;
- ;; (*) The possible value for the crypto hash identifiers can be found below:
- ;;
- ;; | value | Algorithm | Bit Width |
- ;; |-------|-----------|-----------|
- ;; | 0 | SHA2 | 256 |
- ;; | 1 | KECCAK | 256 |
- ;; | 2 | BLAKE2 | 256 |
- ;; | 3 | BLAKE2 | 128 |
- ;; ---------------------------------
- (func (export "call")
- (local $chosen_hash_fn i32)
- (local $input_len_ptr i32)
- (local $input_ptr i32)
- (local $input_len i32)
- (local $output_ptr i32)
- (local $output_len i32)
- (local.set $input_len_ptr (i32.const 256))
- (local.set $input_ptr (i32.const 10))
- (i32.store (local.get $input_len_ptr) (i32.const 246))
- (call $seal_input (local.get $input_ptr) (local.get $input_len_ptr))
- (local.set $chosen_hash_fn (i32.load8_u (local.get $input_ptr)))
- (if (i32.gt_u (local.get $chosen_hash_fn) (i32.const 7))
- ;; We check that the chosen hash fn identifier is within bounds: [0,7]
- (unreachable)
- )
- (local.set $input_ptr (i32.add (local.get $input_ptr) (i32.const 1)))
- (local.set $input_len (i32.sub (i32.load (local.get $input_len_ptr)) (i32.const 1)))
- (local.set $output_len (i32.load8_u (local.get $chosen_hash_fn)))
- (call_indirect (type $hash_fn_sig)
- (local.get $input_ptr)
- (local.get $input_len)
- (local.get $input_ptr)
- (local.get $chosen_hash_fn) ;; Which crypto hash function to execute.
- )
- (call $seal_return
- (i32.const 0)
- (local.get $input_ptr) ;; Linear memory location of the output buffer.
- (local.get $output_len) ;; Number of output buffer bytes.
- )
- (unreachable)
- )
-)
pallets/contracts/fixtures/destroy_and_transfer.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/destroy_and_transfer.wat
+++ /dev/null
@@ -1,161 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32) (result i32)))
- (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
- (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_transfer" (func $seal_transfer (param i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_instantiate" (func $seal_instantiate
- (param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)
- ))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 8) Endowment to send when creating contract.
- (data (i32.const 0) "\00\00\01")
-
- ;; [8, 16) Value to send when calling contract.
-
- ;; [16, 48) The key to store the contract address under.
-
- ;; [48, 80) Buffer where to store the input to the contract
-
- ;; [88, 96) Size of the buffer
- (data (i32.const 88) "\FF")
-
- ;; [96, 100) Size of the input buffer
- (data (i32.const 96) "\20")
-
- ;; [100, 132) Buffer where to store the address of the instantiated contract
-
- ;; [132, 134) Salt
- (data (i32.const 132) "\47\11")
-
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "deploy")
- ;; Input data is the code hash of the contract to be deployed.
- (call $seal_input (i32.const 48) (i32.const 96))
- (call $assert
- (i32.eq
- (i32.load (i32.const 96))
- (i32.const 32)
- )
- )
-
- ;; Deploy the contract with the provided code hash.
- (call $assert
- (i32.eq
- (call $seal_instantiate
- (i32.const 48) ;; Pointer to the code hash.
- (i32.const 32) ;; Length of the code hash.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 0) ;; Pointer to input data buffer address
- (i32.const 0) ;; Length of input data buffer
- (i32.const 100) ;; Buffer where to store address of new contract
- (i32.const 88) ;; Pointer to the length of the buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 132) ;; salt_ptr
- (i32.const 2) ;; salt_len
- )
- (i32.const 0)
- )
- )
-
- ;; Check that address has expected length
- (call $assert
- (i32.eq
- (i32.load (i32.const 88))
- (i32.const 32)
- )
- )
-
- ;; Store the return address.
- (call $seal_set_storage
- (i32.const 16) ;; Pointer to the key
- (i32.const 100) ;; Pointer to the value
- (i32.const 32) ;; Length of the value
- )
- )
-
- (func (export "call")
- ;; Read address of destination contract from storage.
- (call $assert
- (i32.eq
- (call $seal_get_storage
- (i32.const 16) ;; Pointer to the key
- (i32.const 100) ;; Pointer to the value
- (i32.const 88) ;; Pointer to the len of the value
- )
- (i32.const 0)
- )
- )
- (call $assert
- (i32.eq
- (i32.load (i32.const 88))
- (i32.const 32)
- )
- )
-
- ;; Calling the destination contract with non-empty input data should fail.
- (call $assert
- (i32.eq
- (call $seal_call
- (i32.const 100) ;; Pointer to destination address
- (i32.const 32) ;; Length of destination address
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer
- (i32.const 0) ;; Pointer to input data buffer address
- (i32.const 1) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
-
- )
- (i32.const 0x1)
- )
- )
-
- ;; Call the destination contract regularly, forcing it to self-destruct.
- (call $assert
- (i32.eq
- (call $seal_call
- (i32.const 100) ;; Pointer to destination address
- (i32.const 32) ;; Length of destination address
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 8) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer
- (i32.const 0) ;; Pointer to input data buffer address
- (i32.const 0) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- )
- (i32.const 0)
- )
- )
-
- ;; Calling the destination address with non-empty input data should now work since the
- ;; contract has been removed. Also transfer a balance to the address so we can ensure this
- ;; does not keep the contract alive.
- (call $assert
- (i32.eq
- (call $seal_transfer
- (i32.const 100) ;; Pointer to destination address
- (i32.const 32) ;; Length of destination address
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer
- )
- (i32.const 0)
- )
- )
- )
-)
pallets/contracts/fixtures/drain.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/drain.wat
+++ /dev/null
@@ -1,49 +0,0 @@
-(module
- (import "seal0" "seal_balance" (func $seal_balance (param i32 i32)))
- (import "seal0" "seal_transfer" (func $seal_transfer (param i32 i32 i32 i32) (result i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 8) reserved for $seal_balance output
-
- ;; [8, 16) length of the buffer
- (data (i32.const 8) "\08")
-
- ;; [16, inf) zero initialized
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "deploy"))
-
- (func (export "call")
- ;; Send entire remaining balance to the 0 address.
- (call $seal_balance (i32.const 0) (i32.const 8))
-
- ;; Balance should be encoded as a u64.
- (call $assert
- (i32.eq
- (i32.load (i32.const 8))
- (i32.const 8)
- )
- )
-
- ;; Self-destruct by sending full balance to the 0 address.
- (call $assert
- (i32.eq
- (call $seal_transfer
- (i32.const 16) ;; Pointer to destination address
- (i32.const 32) ;; Length of destination address
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer
- )
- (i32.const 4) ;; ReturnCode::BelowSubsistenceThreshold
- )
- )
- )
-)
pallets/contracts/fixtures/event_size.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/event_size.wat
+++ /dev/null
@@ -1,39 +0,0 @@
-(module
- (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "env" "memory" (memory 16 16))
-
- ;; [0, 4) size of the input buffer
- (data (i32.const 0) "\04")
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "call")
- (call $seal_input (i32.const 4) (i32.const 0))
-
- ;; assert input size == 4
- (call $assert
- (i32.eq
- (i32.load (i32.const 0))
- (i32.const 4)
- )
- )
-
- ;; place a garbage value in storage, the size of which is specified by the call input.
- (call $seal_deposit_event
- (i32.const 0) ;; topics_ptr
- (i32.const 0) ;; topics_len
- (i32.const 0) ;; data_ptr
- (i32.load (i32.const 4)) ;; data_len
- )
- )
-
- (func (export "deploy"))
-)
pallets/contracts/fixtures/instantiate_return_code.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/instantiate_return_code.wat
+++ /dev/null
@@ -1,49 +0,0 @@
-;; This instantiats a contract and transfers 100 balance during this call and copies the return code
-;; of this call to the output buffer.
-;; The first 32 byte of input is the code hash to instantiate
-;; The rest of the input is forwarded to the constructor of the callee
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_instantiate" (func $seal_instantiate
- (param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)
- ))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 8) 10_000 balance
- (data (i32.const 0) "\10\27\00\00\00\00\00\00")
-
- ;; [8, 12) here we store the return code of the transfer
-
- ;; [12, 16) size of the input buffer
- (data (i32.const 12) "\24")
-
- ;; [16, inf) input buffer
- ;; 32 bye code hash + 4 byte forward
-
- (func (export "deploy"))
-
- (func (export "call")
- (call $seal_input (i32.const 16) (i32.const 12))
- (i32.store
- (i32.const 8)
- (call $seal_instantiate
- (i32.const 16) ;; Pointer to the code hash.
- (i32.const 32) ;; Length of the code hash.
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 0) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer.
- (i32.const 48) ;; Pointer to input data buffer address
- (i32.const 4) ;; Length of input data buffer
- (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy address
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- (i32.const 0) ;; salt_ptr
- (i32.const 0) ;; salt_len
- )
- )
- ;; exit with success and take transfer return code to the output buffer
- (call $seal_return (i32.const 0) (i32.const 8) (i32.const 4))
- )
-)
pallets/contracts/fixtures/ok_trap_revert.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/ok_trap_revert.wat
+++ /dev/null
@@ -1,35 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- (func (export "deploy")
- (call $ok_trap_revert)
- )
-
- (func (export "call")
- (call $ok_trap_revert)
- )
-
- (func $ok_trap_revert
- (i32.store (i32.const 4) (i32.const 4))
- (call $seal_input (i32.const 0) (i32.const 4))
- (block $IF_2
- (block $IF_1
- (block $IF_0
- (br_table $IF_0 $IF_1 $IF_2
- (i32.load8_u (i32.const 0))
- )
- (unreachable)
- )
- ;; 0 = return with success
- return
- )
- ;; 1 = revert
- (call $seal_return (i32.const 1) (i32.const 0) (i32.const 0))
- (unreachable)
- )
- ;; 2 = trap
- (unreachable)
- )
-)
\ No newline at end of file
pallets/contracts/fixtures/restoration.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/restoration.wat
+++ /dev/null
@@ -1,75 +0,0 @@
-(module
- (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_restore_to"
- (func $seal_restore_to
- (param i32 i32 i32 i32 i32 i32 i32 i32)
- )
- )
- (import "env" "memory" (memory 1 1))
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "call")
- ;; copy code hash to contract memory
- (call $seal_input (i32.const 308) (i32.const 304))
- (call $assert
- (i32.eq
- (i32.load (i32.const 304))
- (i32.const 64)
- )
- )
- (call $seal_restore_to
- ;; Pointer and length of the encoded dest buffer.
- (i32.const 340)
- (i32.const 32)
- ;; Pointer and length of the encoded code hash buffer
- (i32.const 308)
- (i32.const 32)
- ;; Pointer and length of the encoded rent_allowance buffer
- (i32.const 296)
- (i32.const 8)
- ;; Pointer and number of items in the delta buffer.
- ;; This buffer specifies multiple keys for removal before restoration.
- (i32.const 100)
- (i32.const 1)
- )
- )
- (func (export "deploy")
- ;; Data to restore
- (call $seal_set_storage
- (i32.const 0)
- (i32.const 0)
- (i32.const 4)
- )
-
- ;; ACL
- (call $seal_set_storage
- (i32.const 100)
- (i32.const 0)
- (i32.const 4)
- )
- )
-
- ;; Data to restore
- (data (i32.const 0) "\28")
-
- ;; Buffer that has ACL storage keys.
- (data (i32.const 100) "\01")
-
- ;; [296, 304) Rent allowance
- (data (i32.const 296) "\32\00\00\00\00\00\00\00")
-
- ;; [304, 308) Size of the buffer that holds code_hash + addr
- (data (i32.const 304) "\40")
-
- ;; [308, 340) code hash of bob (copied by seal_input)
- ;; [340, 372) addr of bob (copied by seal_input)
-)
pallets/contracts/fixtures/return_from_start_fn.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/return_from_start_fn.wat
+++ /dev/null
@@ -1,28 +0,0 @@
-(module
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- (start $start)
- (func $start
- (call $seal_deposit_event
- (i32.const 0) ;; The topics buffer
- (i32.const 0) ;; The topics buffer's length
- (i32.const 8) ;; The data buffer
- (i32.const 4) ;; The data buffer's length
- )
- (call $seal_return
- (i32.const 0)
- (i32.const 8)
- (i32.const 4)
- )
- (unreachable)
- )
-
- (func (export "call")
- (unreachable)
- )
- (func (export "deploy"))
-
- (data (i32.const 8) "\01\02\03\04")
-)
pallets/contracts/fixtures/return_with_data.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/return_with_data.wat
+++ /dev/null
@@ -1,33 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 128) buffer where input is copied
-
- ;; [128, 132) length of the input buffer
- (data (i32.const 128) "\80")
-
- ;; Deploy routine is the same as call.
- (func (export "deploy")
- (call $call)
- )
-
- ;; Call reads the first 4 bytes (LE) as the exit status and returns the rest as output data.
- (func $call (export "call")
- ;; Copy input into this contracts memory.
- (call $seal_input (i32.const 0) (i32.const 128))
-
- ;; Copy all but the first 4 bytes of the input data as the output data.
- ;; Use the first byte as exit status
- (call $seal_return
- (i32.load8_u (i32.const 0)) ;; Exit status
- (i32.const 4) ;; Pointer to the data to return.
- (i32.sub ;; Count of bytes to copy.
- (i32.load (i32.const 128))
- (i32.const 4)
- )
- )
- (unreachable)
- )
-)
pallets/contracts/fixtures/run_out_of_gas.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/run_out_of_gas.wat
+++ /dev/null
@@ -1,7 +0,0 @@
-(module
- (func (export "call")
- (loop $inf (br $inf)) ;; just run out of gas
- (unreachable)
- )
- (func (export "deploy"))
-)
pallets/contracts/fixtures/self_destruct.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/self_destruct.wat
+++ /dev/null
@@ -1,83 +0,0 @@
-(module
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "seal0" "seal_address" (func $seal_address (param i32 i32)))
- (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 32) reserved for $seal_address output
-
- ;; [32, 36) length of the buffer
- (data (i32.const 32) "\20")
-
- ;; [36, 68) Address of django
- (data (i32.const 36)
- "\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04"
- "\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04\04"
- )
-
- ;; [68, 72) reserved for output of $seal_input
-
- ;; [72, 76) length of the buffer
- (data (i32.const 72) "\04")
-
- ;; [76, inf) zero initialized
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "deploy"))
-
- (func (export "call")
- ;; If the input data is not empty, then recursively call self with empty input data.
- ;; This should trap instead of self-destructing since a contract cannot be removed live in
- ;; the execution stack cannot be removed. If the recursive call traps, then trap here as
- ;; well.
- (call $seal_input (i32.const 68) (i32.const 72))
- (if (i32.load (i32.const 72))
- (then
- (call $seal_address (i32.const 0) (i32.const 32))
-
- ;; Expect address to be 8 bytes.
- (call $assert
- (i32.eq
- (i32.load (i32.const 32))
- (i32.const 32)
- )
- )
-
- ;; Recursively call self with empty input data.
- (call $assert
- (i32.eq
- (call $seal_call
- (i32.const 0) ;; Pointer to own address
- (i32.const 32) ;; Length of own address
- (i64.const 0) ;; How much gas to devote for the execution. 0 = all.
- (i32.const 76) ;; Pointer to the buffer with value to transfer
- (i32.const 8) ;; Length of the buffer with value to transfer
- (i32.const 0) ;; Pointer to input data buffer address
- (i32.const 0) ;; Length of input data buffer
- (i32.const 4294967295) ;; u32 max sentinel value: do not copy output
- (i32.const 0) ;; Length is ignored in this case
- )
- (i32.const 0)
- )
- )
- )
- (else
- ;; Try to terminate and give balance to django.
- (call $seal_terminate
- (i32.const 36) ;; Pointer to beneficiary address
- (i32.const 32) ;; Length of beneficiary address
- )
- (unreachable) ;; seal_terminate never returns
- )
- )
- )
-)
pallets/contracts/fixtures/self_destructing_constructor.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/self_destructing_constructor.wat
+++ /dev/null
@@ -1,23 +0,0 @@
-(module
- (import "seal0" "seal_terminate" (func $seal_terminate (param i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "deploy")
- ;; Self-destruct by sending full balance to the 0 address.
- (call $seal_terminate
- (i32.const 0) ;; Pointer to destination address
- (i32.const 32) ;; Length of destination address
- )
- )
-
- (func (export "call"))
-)
pallets/contracts/fixtures/set_empty_storage.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/set_empty_storage.wat
+++ /dev/null
@@ -1,15 +0,0 @@
-;; This module stores a KV pair into the storage
-(module
- (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
- (import "env" "memory" (memory 16 16))
-
- (func (export "call")
- )
- (func (export "deploy")
- (call $seal_set_storage
- (i32.const 0) ;; Pointer to storage key
- (i32.const 0) ;; Pointer to value
- (i32.load (i32.const 0)) ;; Size of value
- )
- )
-)
pallets/contracts/fixtures/set_rent.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/set_rent.wat
+++ /dev/null
@@ -1,105 +0,0 @@
-(module
- (import "seal0" "seal_transfer" (func $seal_transfer (param i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
- (import "seal0" "seal_clear_storage" (func $seal_clear_storage (param i32)))
- (import "seal0" "seal_set_rent_allowance" (func $seal_set_rent_allowance (param i32 i32)))
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; insert a value of 4 bytes into storage
- (func $call_0
- (call $seal_set_storage
- (i32.const 1)
- (i32.const 0)
- (i32.const 4)
- )
- )
-
- ;; remove the value inserted by call_1
- (func $call_1
- (call $seal_clear_storage
- (i32.const 1)
- )
- )
-
- ;; transfer 50 to CHARLIE
- (func $call_2
- (call $assert
- (i32.eq
- (call $seal_transfer (i32.const 136) (i32.const 32) (i32.const 100) (i32.const 8))
- (i32.const 0)
- )
- )
- )
-
- ;; do nothing
- (func $call_else)
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- ;; Dispatch the call according to input size
- (func (export "call")
- (local $input_size i32)
- ;; 4 byte i32 for br_table followed by 32 byte destination for transfer
- (i32.store (i32.const 128) (i32.const 36))
- (call $seal_input (i32.const 132) (i32.const 128))
- (set_local $input_size
- (i32.load (i32.const 132))
- )
- (block $IF_ELSE
- (block $IF_2
- (block $IF_1
- (block $IF_0
- (br_table $IF_0 $IF_1 $IF_2 $IF_ELSE
- (get_local $input_size)
- )
- (unreachable)
- )
- (call $call_0)
- return
- )
- (call $call_1)
- return
- )
- (call $call_2)
- return
- )
- (call $call_else)
- )
-
- ;; Set into storage a 4 bytes value
- ;; Set call set_rent_allowance with input
- (func (export "deploy")
- (call $seal_set_storage
- (i32.const 0)
- (i32.const 0)
- (i32.const 4)
- )
- (i32.store (i32.const 128) (i32.const 64))
- (call $seal_input
- (i32.const 132)
- (i32.const 128)
- )
- (call $seal_set_rent_allowance
- (i32.const 132)
- (i32.load (i32.const 128))
- )
- )
-
- ;; Encoding of 10 in balance
- (data (i32.const 0) "\28")
-
- ;; encoding of 50 balance
- (data (i32.const 100) "\32")
-
- ;; [128, 132) size of seal input buffer
-
- ;; [132, inf) output buffer for seal input
-)
pallets/contracts/fixtures/storage_size.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/storage_size.wat
+++ /dev/null
@@ -1,68 +0,0 @@
-(module
- (import "seal0" "seal_get_storage" (func $seal_get_storage (param i32 i32 i32) (result i32)))
- (import "seal0" "seal_set_storage" (func $seal_set_storage (param i32 i32 i32)))
- (import "seal0" "seal_input" (func $seal_input (param i32 i32)))
- (import "env" "memory" (memory 16 16))
-
- ;; [0, 32) storage key
- (data (i32.const 0) "\01")
-
- ;; [32, 36) buffer where input is copied (expected size of storage item)
-
- ;; [36, 40) size of the input buffer
- (data (i32.const 36) "\04")
-
- ;; [40, 44) size of buffer for seal_get_storage set to max
- (data (i32.const 40) "\FF\FF\FF\FF")
-
- ;; [44, inf) seal_get_storage buffer
-
- (func $assert (param i32)
- (block $ok
- (br_if $ok
- (get_local 0)
- )
- (unreachable)
- )
- )
-
- (func (export "call")
- (call $seal_input (i32.const 32) (i32.const 36))
-
- ;; assert input size == 4
- (call $assert
- (i32.eq
- (i32.load (i32.const 36))
- (i32.const 4)
- )
- )
-
- ;; place a garbage value in storage, the size of which is specified by the call input.
- (call $seal_set_storage
- (i32.const 0) ;; Pointer to storage key
- (i32.const 0) ;; Pointer to value
- (i32.load (i32.const 32)) ;; Size of value
- )
-
- (call $assert
- (i32.eq
- (call $seal_get_storage
- (i32.const 0) ;; Pointer to storage key
- (i32.const 44) ;; buffer where to copy result
- (i32.const 40) ;; pointer to size of buffer
- )
- (i32.const 0)
- )
- )
-
- (call $assert
- (i32.eq
- (i32.load (i32.const 40))
- (i32.load (i32.const 32))
- )
- )
- )
-
- (func (export "deploy"))
-
-)
pallets/contracts/fixtures/transfer_return_code.watdiffbeforeafterboth--- a/pallets/contracts/fixtures/transfer_return_code.wat
+++ /dev/null
@@ -1,34 +0,0 @@
-;; This transfers 100 balance to the zero account and copies the return code
-;; of this transfer to the output buffer.
-(module
- (import "seal0" "seal_transfer" (func $seal_transfer (param i32 i32 i32 i32) (result i32)))
- (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
- (import "env" "memory" (memory 1 1))
-
- ;; [0, 32) zero-adress
- (data (i32.const 0)
- "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
- "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
- )
-
- ;; [32, 40) 100 balance
- (data (i32.const 32) "\64\00\00\00\00\00\00\00")
-
- ;; [40, 44) here we store the return code of the transfer
-
- (func (export "deploy"))
-
- (func (export "call")
- (i32.store
- (i32.const 40)
- (call $seal_transfer
- (i32.const 0) ;; ptr to destination address
- (i32.const 32) ;; length of destination address
- (i32.const 32) ;; ptr to value to transfer
- (i32.const 8) ;; length of value to transfer
- )
- )
- ;; exit with success and take transfer return code to the output buffer
- (call $seal_return (i32.const 0) (i32.const 40) (i32.const 4))
- )
-)
pallets/contracts/proc-macro/Cargo.tomldiffbeforeafterboth--- a/pallets/contracts/proc-macro/Cargo.toml
+++ /dev/null
@@ -1,26 +0,0 @@
-[package]
-name = "pallet-contracts-proc-macro"
-version = "3.0.0"
-authors = ["Parity Technologies <admin@parity.io>"]
-edition = "2018"
-license = "Apache-2.0"
-homepage = "https://substrate.dev"
-repository = "https://github.com/paritytech/substrate/"
-description = "Procedural macros used in pallet_contracts"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[lib]
-proc-macro = true
-
-[dependencies]
-proc-macro2 = "1"
-quote = "1"
-syn = "1"
-
-[dev-dependencies]
-
-[features]
-# If set the full output is generated. Do NOT set when generating for wasm runtime.
-full = []
pallets/contracts/proc-macro/src/lib.rsdiffbeforeafterboth--- a/pallets/contracts/proc-macro/src/lib.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-//! Proc macros used in the contracts module.
-
-#![no_std]
-
-extern crate alloc;
-
-use proc_macro2::TokenStream;
-use quote::{quote, quote_spanned};
-use syn::spanned::Spanned;
-use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Ident};
-use alloc::string::ToString;
-
-/// This derives `Debug` for a struct where each field must be of some numeric type.
-/// It interprets each field as its represents some weight and formats it as times so that
-/// it is readable by humans.
-#[proc_macro_derive(WeightDebug)]
-pub fn derive_weight_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- derive_debug(input, format_weight)
-}
-
-/// This is basically identical to the std libs Debug derive but without adding any
-/// bounds to existing generics.
-#[proc_macro_derive(ScheduleDebug)]
-pub fn derive_schedule_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- derive_debug(input, format_default)
-}
-
-fn derive_debug(
- input: proc_macro::TokenStream,
- fmt: impl Fn(&Ident) -> TokenStream
-) -> proc_macro::TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
- let data = if let Data::Struct(data) = &input.data {
- data
- } else {
- return quote_spanned! {
- name.span() =>
- compile_error!("WeightDebug is only supported for structs.");
- }.into();
- };
-
- #[cfg(feature = "full")]
- let fields = iterate_fields(data, fmt);
-
- #[cfg(not(feature = "full"))]
- let fields = {
- drop(fmt);
- drop(data);
- TokenStream::new()
- };
-
- let tokens = quote! {
- impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause {
- fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- use ::sp_runtime::{FixedPointNumber, FixedU128 as Fixed};
- let mut formatter = formatter.debug_struct(stringify!(#name));
- #fields
- formatter.finish()
- }
- }
- };
-
- tokens.into()
-}
-
-/// This is only used then the `full` feature is activated.
-#[cfg(feature = "full")]
-fn iterate_fields(data: &DataStruct, fmt: impl Fn(&Ident) -> TokenStream) -> TokenStream {
- match &data.fields {
- Fields::Named(fields) => {
- let recurse = fields.named
- .iter()
- .filter_map(|f| {
- let name = f.ident.as_ref()?;
- if name.to_string().starts_with('_') {
- return None;
- }
- let value = fmt(name);
- let ret = quote_spanned!{ f.span() =>
- formatter.field(stringify!(#name), #value);
- };
- Some(ret)
- });
- quote!{
- #( #recurse )*
- }
- }
- Fields::Unnamed(fields) => quote_spanned!{
- fields.span() =>
- compile_error!("Unnamed fields are not supported")
- },
- Fields::Unit => quote!(),
- }
-}
-
-fn format_weight(field: &Ident) -> TokenStream {
- quote_spanned! { field.span() =>
- &if self.#field > 1_000_000_000 {
- format!(
- "{:.1?} ms",
- Fixed::saturating_from_rational(self.#field, 1_000_000_000).to_float()
- )
- } else if self.#field > 1_000_000 {
- format!(
- "{:.1?} µs",
- Fixed::saturating_from_rational(self.#field, 1_000_000).to_float()
- )
- } else if self.#field > 1_000 {
- format!(
- "{:.1?} ns",
- Fixed::saturating_from_rational(self.#field, 1_000).to_float()
- )
- } else {
- format!("{} ps", self.#field)
- }
- }
-}
-
-fn format_default(field: &Ident) -> TokenStream {
- quote_spanned! { field.span() =>
- &self.#field
- }
-}
pallets/contracts/rpc/Cargo.tomldiffbeforeafterboth--- a/pallets/contracts/rpc/Cargo.toml
+++ /dev/null
@@ -1,32 +0,0 @@
-[package]
-name = "pallet-contracts-rpc"
-version = "3.0.0"
-authors = ["Parity Technologies <admin@parity.io>"]
-edition = "2018"
-license = "Apache-2.0"
-homepage = "https://substrate.dev"
-repository = "https://github.com/paritytech/substrate/"
-description = "Node-specific RPC methods for interaction with contracts."
-readme = "README.md"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[dependencies]
-codec = { package = "parity-scale-codec", version = "2" }
-jsonrpc-core = "15"
-jsonrpc-core-client = "15"
-jsonrpc-derive = "15"
-serde = { version = "1", features = ["derive"] }
-
-# Substrate Dependencies
-pallet-contracts-primitives = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts-rpc-runtime-api = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-api = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-blockchain = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-core = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-rpc = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-runtime = { version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[dev-dependencies]
-serde_json = "1"
pallets/contracts/rpc/README.mddiffbeforeafterbothno changes
pallets/contracts/rpc/runtime-api/Cargo.tomldiffbeforeafterboth--- a/pallets/contracts/rpc/runtime-api/Cargo.toml
+++ /dev/null
@@ -1,32 +0,0 @@
-[package]
-name = "pallet-contracts-rpc-runtime-api"
-version = "3.0.0"
-authors = ["Parity Technologies <admin@parity.io>"]
-edition = "2018"
-license = "Apache-2.0"
-homepage = "https://substrate.dev"
-repository = "https://github.com/paritytech/substrate/"
-description = "Runtime API definition required by Contracts RPC extensions."
-readme = "README.md"
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[dependencies]
-codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
-
-# Substrate Dependencies
-pallet-contracts-primitives = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-api = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-std = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[features]
-default = ["std"]
-std = [
- "sp-api/std",
- "codec/std",
- "sp-std/std",
- "sp-runtime/std",
- "pallet-contracts-primitives/std",
-]
pallets/contracts/rpc/runtime-api/README.mddiffbeforeafterboth--- a/pallets/contracts/rpc/runtime-api/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Runtime API definition required by Contracts RPC extensions.
-
-This API should be imported and implemented by the runtime,
-of a node that wants to use the custom RPC extension
-adding Contracts access methods.
-
-License: Apache-2.0
\ No newline at end of file
pallets/contracts/rpc/runtime-api/src/lib.rsdiffbeforeafterboth--- a/pallets/contracts/rpc/runtime-api/src/lib.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2019-2021 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.
-
-//! Runtime API definition required by Contracts RPC extensions.
-//!
-//! This API should be imported and implemented by the runtime,
-//! of a node that wants to use the custom RPC extension
-//! adding Contracts access methods.
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-use codec::Codec;
-use sp_std::vec::Vec;
-use pallet_contracts_primitives::{
- ContractExecResult, GetStorageResult, RentProjectionResult, Code, ContractInstantiateResult,
-};
-
-sp_api::decl_runtime_apis! {
- /// The API to interact with contracts without using executive.
- pub trait ContractsApi<AccountId, Balance, BlockNumber, Hash> where
- AccountId: Codec,
- Balance: Codec,
- BlockNumber: Codec,
- Hash: Codec,
- {
- /// Perform a call from a specified account to a given contract.
- ///
- /// See [`pallet_contracts::Pallet::call`].
- fn call(
- origin: AccountId,
- dest: AccountId,
- value: Balance,
- gas_limit: u64,
- input_data: Vec<u8>,
- ) -> ContractExecResult;
-
- /// Instantiate a new contract.
- ///
- /// See [`pallet_contracts::Pallet::instantiate`].
- fn instantiate(
- origin: AccountId,
- endowment: Balance,
- gas_limit: u64,
- code: Code<Hash>,
- data: Vec<u8>,
- salt: Vec<u8>,
- ) -> ContractInstantiateResult<AccountId, BlockNumber>;
-
- /// 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 or if the contract is a tombstone, then `Err`
- /// is returned.
- fn get_storage(
- address: AccountId,
- key: [u8; 32],
- ) -> GetStorageResult;
-
- /// Returns the projected time a given contract will be able to sustain paying its rent.
- ///
- /// The returned projection is relevant for the current block, i.e. it is as if the contract
- /// was accessed at the current block.
- ///
- /// Returns `Err` if the contract is in a tombstone state or doesn't exist.
- fn rent_projection(address: AccountId) -> RentProjectionResult<BlockNumber>;
- }
-}
pallets/contracts/rpc/src/lib.rsdiffbeforeafterboth--- a/pallets/contracts/rpc/src/lib.rs
+++ /dev/null
@@ -1,434 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2019-2021 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.
-
-//! Node-specific RPC methods for interaction with contracts.
-
-use std::sync::Arc;
-
-use codec::Codec;
-use jsonrpc_core::{Error, ErrorCode, Result};
-use jsonrpc_derive::rpc;
-use pallet_contracts_primitives::RentProjection;
-use serde::{Deserialize, Serialize};
-use sp_api::ProvideRuntimeApi;
-use sp_blockchain::HeaderBackend;
-use sp_core::{Bytes, H256};
-use sp_rpc::number::NumberOrHex;
-use sp_runtime::{
- generic::BlockId,
- traits::{Block as BlockT, Header as HeaderT},
-};
-use std::convert::{TryFrom, TryInto};
-use pallet_contracts_primitives::{Code, ContractExecResult, ContractInstantiateResult};
-
-pub use pallet_contracts_rpc_runtime_api::ContractsApi as ContractsRuntimeApi;
-
-const RUNTIME_ERROR: i64 = 1;
-const CONTRACT_DOESNT_EXIST: i64 = 2;
-const CONTRACT_IS_A_TOMBSTONE: i64 = 3;
-
-pub type Weight = u64;
-
-/// A rough estimate of how much gas a decent hardware consumes per second,
-/// using native execution.
-/// This value is used to set the upper bound for maximal contract calls to
-/// prevent blocking the RPC for too long.
-///
-/// As 1 gas is equal to 1 weight we base this on the conducted benchmarks which
-/// determined runtime weights:
-/// https://github.com/paritytech/substrate/pull/5446
-const GAS_PER_SECOND: Weight = 1_000_000_000_000;
-
-/// The maximum amount of weight that the call and instantiate rpcs are allowed to consume.
-/// This puts a ceiling on the weight limit that is supplied to the rpc as an argument.
-const GAS_LIMIT: Weight = 5 * GAS_PER_SECOND;
-
-/// A private newtype for converting `ContractAccessError` into an RPC error.
-struct ContractAccessError(pallet_contracts_primitives::ContractAccessError);
-impl From<ContractAccessError> for Error {
- fn from(e: ContractAccessError) -> Error {
- use pallet_contracts_primitives::ContractAccessError::*;
- match e.0 {
- DoesntExist => Error {
- code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST),
- message: "The specified contract doesn't exist.".into(),
- data: None,
- },
- IsTombstone => Error {
- code: ErrorCode::ServerError(CONTRACT_IS_A_TOMBSTONE),
- message: "The contract is a tombstone and doesn't have any storage.".into(),
- data: None,
- },
- }
- }
-}
-
-/// A struct that encodes RPC parameters required for a call to a smart-contract.
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
-pub struct CallRequest<AccountId> {
- origin: AccountId,
- dest: AccountId,
- value: NumberOrHex,
- gas_limit: NumberOrHex,
- input_data: Bytes,
-}
-
-/// A struct that encodes RPC parameters required to instantiate a new smart-contract.
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
-pub struct InstantiateRequest<AccountId, Hash> {
- origin: AccountId,
- endowment: NumberOrHex,
- gas_limit: NumberOrHex,
- code: Code<Hash>,
- data: Bytes,
- salt: Bytes,
-}
-
-/// Contracts RPC methods.
-#[rpc]
-pub trait ContractsApi<BlockHash, BlockNumber, AccountId, Balance, Hash> {
- /// Executes a call to a contract.
- ///
- /// This call is performed locally without submitting any transactions. Thus executing this
- /// won't change any state. Nonetheless, the calling state-changing contracts is still possible.
- ///
- /// This method is useful for calling getter-like methods on contracts.
- #[rpc(name = "contracts_call")]
- fn call(
- &self,
- call_request: CallRequest<AccountId>,
- at: Option<BlockHash>,
- ) -> Result<ContractExecResult>;
-
- /// Instantiate a new contract.
- ///
- /// This call is performed locally without submitting any transactions. Thus the contract
- /// is not actually created.
- ///
- /// This method is useful for UIs to dry-run contract instantiations.
- #[rpc(name = "contracts_instantiate")]
- fn instantiate(
- &self,
- instantiate_request: InstantiateRequest<AccountId, Hash>,
- at: Option<BlockHash>,
- ) -> Result<ContractInstantiateResult<AccountId, BlockNumber>>;
-
- /// Returns the value under a specified storage `key` in a contract given by `address` param,
- /// or `None` if it is not set.
- #[rpc(name = "contracts_getStorage")]
- fn get_storage(
- &self,
- address: AccountId,
- key: H256,
- at: Option<BlockHash>,
- ) -> Result<Option<Bytes>>;
-
- /// Returns the projected time a given contract will be able to sustain paying its rent.
- ///
- /// The returned projection is relevant for the given block, i.e. it is as if the contract was
- /// accessed at the beginning of that block.
- ///
- /// Returns `None` if the contract is exempted from rent.
- #[rpc(name = "contracts_rentProjection")]
- fn rent_projection(
- &self,
- address: AccountId,
- at: Option<BlockHash>,
- ) -> Result<Option<BlockNumber>>;
-}
-
-/// An implementation of contract specific RPC methods.
-pub struct Contracts<C, B> {
- client: Arc<C>,
- _marker: std::marker::PhantomData<B>,
-}
-
-impl<C, B> Contracts<C, B> {
- /// Create new `Contracts` with the given reference to the client.
- pub fn new(client: Arc<C>) -> Self {
- Contracts {
- client,
- _marker: Default::default(),
- }
- }
-}
-impl<C, Block, AccountId, Balance, Hash>
- ContractsApi<
- <Block as BlockT>::Hash,
- <<Block as BlockT>::Header as HeaderT>::Number,
- AccountId,
- Balance,
- Hash,
- > for Contracts<C, Block>
-where
- Block: BlockT,
- C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
- C::Api: ContractsRuntimeApi<
- Block,
- AccountId,
- Balance,
- <<Block as BlockT>::Header as HeaderT>::Number,
- Hash,
- >,
- AccountId: Codec,
- Balance: Codec + TryFrom<NumberOrHex>,
- Hash: Codec,
-{
- fn call(
- &self,
- call_request: CallRequest<AccountId>,
- at: Option<<Block as BlockT>::Hash>,
- ) -> Result<ContractExecResult> {
- let api = self.client.runtime_api();
- let at = BlockId::hash(at.unwrap_or_else(||
- // If the block hash is not supplied assume the best block.
- self.client.info().best_hash));
-
- let CallRequest {
- origin,
- dest,
- value,
- gas_limit,
- input_data,
- } = call_request;
-
- let value: Balance = decode_hex(value, "balance")?;
- let gas_limit: Weight = decode_hex(gas_limit, "weight")?;
- limit_gas(gas_limit)?;
-
- let exec_result = api
- .call(&at, origin, dest, value, gas_limit, input_data.to_vec())
- .map_err(runtime_error_into_rpc_err)?;
-
- Ok(exec_result)
- }
-
- fn instantiate(
- &self,
- instantiate_request: InstantiateRequest<AccountId, Hash>,
- at: Option<<Block as BlockT>::Hash>,
- ) -> Result<ContractInstantiateResult<AccountId, <<Block as BlockT>::Header as HeaderT>::Number>> {
- let api = self.client.runtime_api();
- let at = BlockId::hash(at.unwrap_or_else(||
- // If the block hash is not supplied assume the best block.
- self.client.info().best_hash));
-
- let InstantiateRequest {
- origin,
- endowment,
- gas_limit,
- code,
- data,
- salt,
- } = instantiate_request;
-
- let endowment: Balance = decode_hex(endowment, "balance")?;
- let gas_limit: Weight = decode_hex(gas_limit, "weight")?;
- limit_gas(gas_limit)?;
-
- let exec_result = api
- .instantiate(&at, origin, endowment, gas_limit, code, data.to_vec(), salt.to_vec())
- .map_err(runtime_error_into_rpc_err)?;
-
- Ok(exec_result)
- }
-
- fn get_storage(
- &self,
- address: AccountId,
- key: H256,
- at: Option<<Block as BlockT>::Hash>,
- ) -> Result<Option<Bytes>> {
- let api = self.client.runtime_api();
- let at = BlockId::hash(at.unwrap_or_else(||
- // If the block hash is not supplied assume the best block.
- self.client.info().best_hash));
-
- let result = api
- .get_storage(&at, address, key.into())
- .map_err(runtime_error_into_rpc_err)?
- .map_err(ContractAccessError)?
- .map(Bytes);
-
- Ok(result)
- }
-
- fn rent_projection(
- &self,
- address: AccountId,
- at: Option<<Block as BlockT>::Hash>,
- ) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
- let api = self.client.runtime_api();
- let at = BlockId::hash(at.unwrap_or_else(||
- // If the block hash is not supplied assume the best block.
- self.client.info().best_hash));
-
- let result = api
- .rent_projection(&at, address)
- .map_err(runtime_error_into_rpc_err)?
- .map_err(ContractAccessError)?;
-
- Ok(match result {
- RentProjection::NoEviction => None,
- RentProjection::EvictionAt(block_num) => Some(block_num),
- })
- }
-}
-
-/// Converts a runtime trap into an RPC error.
-fn runtime_error_into_rpc_err(err: impl std::fmt::Debug) -> Error {
- Error {
- code: ErrorCode::ServerError(RUNTIME_ERROR),
- message: "Runtime error".into(),
- data: Some(format!("{:?}", err).into()),
- }
-}
-
-fn decode_hex<H: std::fmt::Debug + Copy, T: TryFrom<H>>(from: H, name: &str) -> Result<T> {
- from.try_into().map_err(|_| Error {
- code: ErrorCode::InvalidParams,
- message: format!("{:?} does not fit into the {} type", from, name),
- data: None,
- })
-}
-
-fn limit_gas(gas_limit: Weight) -> Result<()> {
- if gas_limit > GAS_LIMIT {
- Err(Error {
- code: ErrorCode::InvalidParams,
- message: format!(
- "Requested gas limit is greater than maximum allowed: {} > {}",
- gas_limit, GAS_LIMIT
- ),
- data: None,
- })
- } else {
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use sp_core::U256;
-
- fn trim(json: &str) -> String {
- json.chars().filter(|c| !c.is_whitespace()).collect()
- }
-
- #[test]
- fn call_request_should_serialize_deserialize_properly() {
- type Req = CallRequest<String>;
- let req: Req = serde_json::from_str(r#"
- {
- "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
- "dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom",
- "value": "0x112210f4B16c1cb1",
- "gasLimit": 1000000000000,
- "inputData": "0x8c97db39"
- }
- "#).unwrap();
- assert_eq!(req.gas_limit.into_u256(), U256::from(0xe8d4a51000u64));
- assert_eq!(req.value.into_u256(), U256::from(1234567890987654321u128));
- }
-
- #[test]
- fn instantiate_request_should_serialize_deserialize_properly() {
- type Req = InstantiateRequest<String, String>;
- let req: Req = serde_json::from_str(r#"
- {
- "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
- "endowment": "0x88",
- "gasLimit": 42,
- "code": { "existing": "0x1122" },
- "data": "0x4299",
- "salt": "0x9988"
- }
- "#).unwrap();
-
- assert_eq!(req.origin, "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL");
- assert_eq!(req.endowment.into_u256(), 0x88.into());
- assert_eq!(req.gas_limit.into_u256(), 42.into());
- assert_eq!(&*req.data, [0x42, 0x99].as_ref());
- assert_eq!(&*req.salt, [0x99, 0x88].as_ref());
- let code = match req.code {
- Code::Existing(hash) => hash,
- _ => panic!("json encoded an existing hash"),
- };
- assert_eq!(&code, "0x1122");
- }
-
- #[test]
- fn call_result_should_serialize_deserialize_properly() {
- fn test(expected: &str) {
- let res: ContractExecResult = serde_json::from_str(expected).unwrap();
- let actual = serde_json::to_string(&res).unwrap();
- assert_eq!(actual, trim(expected).as_str());
- }
- test(r#"{
- "gasConsumed": 5000,
- "debugMessage": "0x68656c704f6b",
- "result": {
- "Ok": {
- "flags": 5,
- "data": "0x1234"
- }
- }
- }"#);
- test(r#"{
- "gasConsumed": 3400,
- "debugMessage": "0x68656c70457272",
- "result": {
- "Err": "BadOrigin"
- }
- }"#);
- }
-
- #[test]
- fn instantiate_result_should_serialize_deserialize_properly() {
- fn test(expected: &str) {
- let res: ContractInstantiateResult<String, u64> = serde_json::from_str(expected).unwrap();
- let actual = serde_json::to_string(&res).unwrap();
- assert_eq!(actual, trim(expected).as_str());
- }
- test(r#"{
- "gasConsumed": 5000,
- "debugMessage": "0x68656c704f6b",
- "result": {
- "Ok": {
- "result": {
- "flags": 5,
- "data": "0x1234"
- },
- "accountId": "5CiPP",
- "rentProjection": null
- }
- }
- }"#);
- test(r#"{
- "gasConsumed": 3400,
- "debugMessage": "0x68656c70457272",
- "result": {
- "Err": "BadOrigin"
- }
- }"#);
- }
-}
pallets/contracts/src/benchmarking/code.rsdiffbeforeafterboth--- a/pallets/contracts/src/benchmarking/code.rs
+++ /dev/null
@@ -1,526 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-//! Functions to procedurally construct contract code used for benchmarking.
-//!
-//! In order to be able to benchmark events that are triggered by contract execution
-//! (API calls into seal, individual instructions), we need to generate contracts that
-//! perform those events. Because those contracts can get very big we cannot simply define
-//! them as text (.wat) as this will be too slow and consume too much memory. Therefore
-//! we define this simple definition of a contract that can be passed to `create_code` that
-//! compiles it down into a `WasmModule` that can be used as a contract's code.
-
-use crate::{Config, CurrentSchedule};
-use parity_wasm::elements::{
- Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
-};
-use pwasm_utils::stack_height::inject_limiter;
-use sp_core::crypto::UncheckedFrom;
-use sp_runtime::traits::Hash;
-use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
-use sp_std::{prelude::*, convert::TryFrom, borrow::ToOwned};
-
-/// Pass to `create_code` in order to create a compiled `WasmModule`.
-///
-/// This exists to have a more declarative way to describe a wasm module than to use
-/// parity-wasm directly. It is tailored to fit the structure of contracts that are
-/// needed for benchmarking.
-#[derive(Default)]
-pub struct ModuleDefinition {
- /// Imported memory attached to the module. No memory is imported if `None`.
- pub memory: Option<ImportedMemory>,
- /// Initializers for the imported memory.
- pub data_segments: Vec<DataSegment>,
- /// Creates the supplied amount of i64 mutable globals initialized with random values.
- pub num_globals: u32,
- /// List of functions that the module should import. They start with index 0.
- pub imported_functions: Vec<ImportedFunction>,
- /// Function body of the exported `deploy` function. Body is empty if `None`.
- /// Its index is `imported_functions.len()`.
- pub deploy_body: Option<FuncBody>,
- /// Function body of the exported `call` function. Body is empty if `None`.
- /// Its index is `imported_functions.len() + 1`.
- pub call_body: Option<FuncBody>,
- /// Function body of a non-exported function with index `imported_functions.len() + 2`.
- pub aux_body: Option<FuncBody>,
- /// The amount of I64 arguments the aux function should have.
- pub aux_arg_num: u32,
- /// If set to true the stack height limiter is injected into the the module. This is
- /// needed for instruction debugging because the cost of executing the stack height
- /// instrumentation should be included in the costs for the individual instructions
- /// that cause more metering code (only call).
- pub inject_stack_metering: bool,
- /// Create a table containing function pointers.
- pub table: Option<TableSegment>,
- /// Create a section named "dummy" of the specified size. This is useful in order to
- /// benchmark the overhead of loading and storing codes of specified sizes. The dummy
- /// section only contributes to the size of the contract but does not affect execution.
- pub dummy_section: u32,
-}
-
-pub struct TableSegment {
- /// How many elements should be created inside the table.
- pub num_elements: u32,
- /// The function index with which all table elements should be initialized.
- pub function_index: u32,
-}
-
-pub struct DataSegment {
- pub offset: u32,
- pub value: Vec<u8>,
-}
-
-#[derive(Clone)]
-pub struct ImportedMemory {
- pub min_pages: u32,
- pub max_pages: u32,
-}
-
-impl ImportedMemory {
- pub fn max<T: Config>() -> Self
- where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
- {
- let pages = max_pages::<T>();
- Self {
- min_pages: pages,
- max_pages: pages,
- }
- }
-}
-
-pub struct ImportedFunction {
- pub name: &'static str,
- pub params: Vec<ValueType>,
- pub return_type: Option<ValueType>,
-}
-
-/// A wasm module ready to be put on chain.
-#[derive(Clone)]
-pub struct WasmModule<T: Config> {
- pub code: Vec<u8>,
- pub hash: <T::Hashing as Hash>::Output,
- memory: Option<ImportedMemory>,
-}
-
-impl<T: Config> From<ModuleDefinition> for WasmModule<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- fn from(def: ModuleDefinition) -> Self {
- // internal functions start at that offset.
- let func_offset = u32::try_from(def.imported_functions.len()).unwrap();
-
- // Every contract must export "deploy" and "call" functions
- let mut contract = parity_wasm::builder::module()
- // deploy function (first internal function)
- .function()
- .signature()
- .build()
- .with_body(
- def.deploy_body
- .unwrap_or_else(|| FuncBody::new(Vec::new(), Instructions::empty())),
- )
- .build()
- // call function (second internal function)
- .function()
- .signature()
- .build()
- .with_body(
- def.call_body
- .unwrap_or_else(|| FuncBody::new(Vec::new(), Instructions::empty())),
- )
- .build()
- .export()
- .field("deploy")
- .internal()
- .func(func_offset)
- .build()
- .export()
- .field("call")
- .internal()
- .func(func_offset + 1)
- .build();
-
- // If specified we add an additional internal function
- if let Some(body) = def.aux_body {
- let mut signature = contract.function().signature();
- for _ in 0..def.aux_arg_num {
- signature = signature.with_param(ValueType::I64);
- }
- contract = signature.build().with_body(body).build();
- }
-
- // Grant access to linear memory.
- if let Some(memory) = &def.memory {
- contract = contract
- .import()
- .module("env")
- .field("memory")
- .external()
- .memory(memory.min_pages, Some(memory.max_pages))
- .build();
- }
-
- // Import supervisor functions. They start with idx 0.
- for func in def.imported_functions {
- let sig = parity_wasm::builder::signature()
- .with_params(func.params)
- .with_results(func.return_type.into_iter().collect())
- .build_sig();
- let sig = contract.push_signature(sig);
- contract = contract
- .import()
- .module("seal0")
- .field(func.name)
- .with_external(parity_wasm::elements::External::Function(sig))
- .build();
- }
-
- // Initialize memory
- for data in def.data_segments {
- contract = contract
- .data()
- .offset(Instruction::I32Const(data.offset as i32))
- .value(data.value)
- .build()
- }
-
- // Add global variables
- if def.num_globals > 0 {
- use rand::{prelude::*, distributions::Standard};
- let rng = rand_pcg::Pcg32::seed_from_u64(3112244599778833558);
- for val in rng.sample_iter(Standard).take(def.num_globals as usize) {
- contract = contract
- .global()
- .value_type()
- .i64()
- .mutable()
- .init_expr(Instruction::I64Const(val))
- .build()
- }
- }
-
- // Add function pointer table
- if let Some(table) = def.table {
- contract = contract
- .table()
- .with_min(table.num_elements)
- .with_max(Some(table.num_elements))
- .with_element(0, vec![table.function_index; table.num_elements as usize])
- .build();
- }
-
- // Add the dummy section
- if def.dummy_section > 0 {
- contract = contract.with_section(Section::Custom(CustomSection::new(
- "dummy".to_owned(),
- vec![42; def.dummy_section as usize],
- )));
- }
-
- let mut code = contract.build();
-
- // Inject stack height metering
- if def.inject_stack_metering {
- code = inject_limiter(code, <CurrentSchedule<T>>::get().limits.stack_height).unwrap();
- }
-
- let code = code.to_bytes().unwrap();
- let hash = T::Hashing::hash(&code);
- Self {
- code,
- hash,
- memory: def.memory,
- }
- }
-}
-
-impl<T: Config> WasmModule<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Creates a wasm module with an empty `call` and `deploy` function and nothing else.
- pub fn dummy() -> Self {
- ModuleDefinition::default().into()
- }
-
- /// Same as `dummy` but with maximum sized linear memory and a dummy section of specified size.
- pub fn dummy_with_bytes(dummy_bytes: u32) -> Self {
- ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- dummy_section: dummy_bytes,
- ..Default::default()
- }
- .into()
- }
-
- /// Creates a wasm module of `target_bytes` size. Used to benchmark the performance of
- /// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes
- /// instrumentation runtime by nesting blocks as deeply as possible given the byte budget.
- pub fn sized(target_bytes: u32) -> Self {
- use parity_wasm::elements::Instruction::{If, I32Const, Return, End};
- // Base size of a contract is 63 bytes and each expansion adds 6 bytes.
- // We do one expansion less to account for the code section and function body
- // size fields inside the binary wasm module representation which are leb128 encoded
- // and therefore grow in size when the contract grows. We are not allowed to overshoot
- // because of the maximum code size that is enforced by `instantiate_with_code`.
- let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1);
- const EXPANSION: [Instruction; 4] = [I32Const(0), If(BlockType::NoResult), Return, End];
- ModuleDefinition {
- call_body: Some(body::repeated(expansions, &EXPANSION)),
- memory: Some(ImportedMemory::max::<T>()),
- ..Default::default()
- }
- .into()
- }
-
- /// Creates a wasm module that calls the imported function named `getter_name` `repeat`
- /// times. The imported function is expected to have the "getter signature" of
- /// (out_ptr: u32, len_ptr: u32) -> ().
- pub fn getter(getter_name: &'static str, repeat: u32) -> Self {
- let pages = max_pages::<T>();
- ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: getter_name,
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- // Write the output buffer size. The output size will be overwritten by the
- // supervisor with the real size when calling the getter. Since this size does not
- // change between calls it suffices to start with an initial value and then just
- // leave as whatever value was written there.
- data_segments: vec![DataSegment {
- offset: 0,
- value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(),
- }],
- call_body: Some(body::repeated(
- repeat,
- &[
- Instruction::I32Const(4), // ptr where to store output
- Instruction::I32Const(0), // ptr to length
- Instruction::Call(0), // call the imported function
- ],
- )),
- ..Default::default()
- }
- .into()
- }
-
- /// Creates a wasm module that calls the imported hash function named `name` `repeat` times
- /// with an input of size `data_size`. Hash functions have the signature
- /// (input_ptr: u32, input_len: u32, output_ptr: u32) -> ()
- pub fn hasher(name: &'static str, repeat: u32, data_size: u32) -> Self {
- ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name,
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- call_body: Some(body::repeated(
- repeat,
- &[
- Instruction::I32Const(0), // input_ptr
- Instruction::I32Const(data_size as i32), // input_len
- Instruction::I32Const(0), // output_ptr
- Instruction::Call(0),
- ],
- )),
- ..Default::default()
- }
- .into()
- }
-
- /// Creates a memory instance for use in a sandbox with dimensions declared in this module
- /// and adds it to `env`. A reference to that memory is returned so that it can be used to
- /// access the memory contents from the supervisor.
- pub fn add_memory<S>(&self, env: &mut EnvironmentDefinitionBuilder<S>) -> Option<Memory> {
- let memory = if let Some(memory) = &self.memory {
- memory
- } else {
- return None;
- };
- let memory = Memory::new(memory.min_pages, Some(memory.max_pages)).unwrap();
- env.add_memory("env", "memory", memory.clone());
- Some(memory)
- }
-
- pub fn unary_instr(instr: Instruction, repeat: u32) -> Self {
- use body::DynInstr::{RandomI64Repeated, Regular};
- ModuleDefinition {
- call_body: Some(body::repeated_dyn(
- repeat,
- vec![
- RandomI64Repeated(1),
- Regular(instr),
- Regular(Instruction::Drop),
- ],
- )),
- ..Default::default()
- }
- .into()
- }
-
- pub fn binary_instr(instr: Instruction, repeat: u32) -> Self {
- use body::DynInstr::{RandomI64Repeated, Regular};
- ModuleDefinition {
- call_body: Some(body::repeated_dyn(
- repeat,
- vec![
- RandomI64Repeated(2),
- Regular(instr),
- Regular(Instruction::Drop),
- ],
- )),
- ..Default::default()
- }
- .into()
- }
-}
-
-/// Mechanisms to generate a function body that can be used inside a `ModuleDefinition`.
-pub mod body {
- use super::*;
-
- /// When generating contract code by repeating a wasm sequence, it's sometimes necessary
- /// to change those instructions on each repetition. The variants of this enum describe
- /// various ways in which this can happen.
- pub enum DynInstr {
- /// Insert the associated instruction.
- Regular(Instruction),
- /// Insert a I32Const with incrementing value for each insertion.
- /// (start_at, increment_by)
- Counter(u32, u32),
- /// Insert a I32Const with a random value in [low, high) not divisible by two.
- /// (low, high)
- RandomUnaligned(u32, u32),
- /// Insert a I32Const with a random value in [low, high).
- /// (low, high)
- RandomI32(i32, i32),
- /// Insert the specified amount of I32Const with a random value.
- RandomI32Repeated(usize),
- /// Insert the specified amount of I64Const with a random value.
- RandomI64Repeated(usize),
- /// Insert a GetLocal with a random offset in [low, high).
- /// (low, high)
- RandomGetLocal(u32, u32),
- /// Insert a SetLocal with a random offset in [low, high).
- /// (low, high)
- RandomSetLocal(u32, u32),
- /// Insert a TeeLocal with a random offset in [low, high).
- /// (low, high)
- RandomTeeLocal(u32, u32),
- /// Insert a GetGlobal with a random offset in [low, high).
- /// (low, high)
- RandomGetGlobal(u32, u32),
- /// Insert a SetGlobal with a random offset in [low, high).
- /// (low, high)
- RandomSetGlobal(u32, u32),
- }
-
- pub fn plain(instructions: Vec<Instruction>) -> FuncBody {
- FuncBody::new(Vec::new(), Instructions::new(instructions))
- }
-
- pub fn repeated(repetitions: u32, instructions: &[Instruction]) -> FuncBody {
- let instructions = Instructions::new(
- instructions
- .iter()
- .cycle()
- .take(instructions.len() * usize::try_from(repetitions).unwrap())
- .cloned()
- .chain(sp_std::iter::once(Instruction::End))
- .collect(),
- );
- FuncBody::new(Vec::new(), instructions)
- }
-
- pub fn repeated_dyn(repetitions: u32, mut instructions: Vec<DynInstr>) -> FuncBody {
- use rand::{prelude::*, distributions::Standard};
-
- // We do not need to be secure here.
- let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615);
-
- // We need to iterate over indices because we cannot cycle over mutable references
- let body = (0..instructions.len())
- .cycle()
- .take(instructions.len() * usize::try_from(repetitions).unwrap())
- .flat_map(|idx| match &mut instructions[idx] {
- DynInstr::Regular(instruction) => vec![instruction.clone()],
- DynInstr::Counter(offset, increment_by) => {
- let current = *offset;
- *offset += *increment_by;
- vec![Instruction::I32Const(current as i32)]
- }
- DynInstr::RandomUnaligned(low, high) => {
- let unaligned = rng.gen_range(*low..*high) | 1;
- vec![Instruction::I32Const(unaligned as i32)]
- }
- DynInstr::RandomI32(low, high) => {
- vec![Instruction::I32Const(rng.gen_range(*low..*high))]
- }
- DynInstr::RandomI32Repeated(num) => (&mut rng)
- .sample_iter(Standard)
- .take(*num)
- .map(|val| Instruction::I32Const(val))
- .collect(),
- DynInstr::RandomI64Repeated(num) => (&mut rng)
- .sample_iter(Standard)
- .take(*num)
- .map(|val| Instruction::I64Const(val))
- .collect(),
- DynInstr::RandomGetLocal(low, high) => {
- vec![Instruction::GetLocal(rng.gen_range(*low..*high))]
- }
- DynInstr::RandomSetLocal(low, high) => {
- vec![Instruction::SetLocal(rng.gen_range(*low..*high))]
- }
- DynInstr::RandomTeeLocal(low, high) => {
- vec![Instruction::TeeLocal(rng.gen_range(*low..*high))]
- }
- DynInstr::RandomGetGlobal(low, high) => {
- vec![Instruction::GetGlobal(rng.gen_range(*low..*high))]
- }
- DynInstr::RandomSetGlobal(low, high) => {
- vec![Instruction::SetGlobal(rng.gen_range(*low..*high))]
- }
- })
- .chain(sp_std::iter::once(Instruction::End))
- .collect();
- FuncBody::new(Vec::new(), Instructions::new(body))
- }
-
- /// Replace the locals of the supplied `body` with `num` i64 locals.
- pub fn inject_locals(body: &mut FuncBody, num: u32) {
- use parity_wasm::elements::Local;
- *body.locals_mut() = (0..num).map(|i| Local::new(i, ValueType::I64)).collect()
- }
-}
-
-/// The maximum amount of pages any contract is allowed to have according to the current `Schedule`.
-pub fn max_pages<T: Config>() -> u32
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- <CurrentSchedule<T>>::get().limits.memory_pages
-}
pallets/contracts/src/benchmarking/mod.rsdiffbeforeafterboth--- a/pallets/contracts/src/benchmarking/mod.rs
+++ /dev/null
@@ -1,2537 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-//! Benchmarks for the contracts pallet
-
-#![cfg(feature = "runtime-benchmarks")]
-
-mod code;
-mod sandbox;
-
-use crate::{
- *, Pallet as Contracts,
- exec::StorageKey,
- rent::Rent,
- schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE},
- storage::Storage,
-};
-use self::{
- code::{
- body::{self, DynInstr::*},
- ModuleDefinition, DataSegment, ImportedMemory, ImportedFunction, WasmModule,
- },
- sandbox::Sandbox,
-};
-use codec::Encode;
-use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
-use frame_system::{Pallet as System, RawOrigin};
-use parity_wasm::elements::{Instruction, ValueType, BlockType};
-use sp_runtime::traits::{Hash, Bounded, Zero};
-use sp_std::{
- default::Default,
- convert::{TryInto},
- vec::Vec,
- vec,
-};
-use pallet_contracts_primitives::RentProjection;
-use frame_support::weights::Weight;
-
-/// How many batches we do per API benchmark.
-const API_BENCHMARK_BATCHES: u32 = 20;
-
-/// How many batches we do per Instruction benchmark.
-const INSTR_BENCHMARK_BATCHES: u32 = 1;
-
-/// An instantiated and deployed contract.
-struct Contract<T: Config> {
- caller: T::AccountId,
- account_id: T::AccountId,
- addr: <T::Lookup as StaticLookup>::Source,
- endowment: BalanceOf<T>,
- code_hash: <T::Hashing as Hash>::Output,
-}
-
-/// Describes how much balance should be transferred on instantiate from the caller.
-enum Endow {
- /// Endow the contract with a maximum amount of balance. This value is described by
- /// `Contract::max_endowment`.
- Max,
- /// Endow so that the amount of balance that is transferred is big but not so big
- /// to offset the rent payment. This is needed in order to test rent collection.
- CollectRent,
-}
-
-impl Endow {
- /// The maximum amount of balance a caller can transfer without being brought below
- /// the existential deposit. This assumes that every caller is funded with the amount
- /// returned by `caller_funding`.
- fn max<T: Config>() -> BalanceOf<T> {
- caller_funding::<T>().saturating_sub(T::Currency::minimum_balance())
- }
-}
-
-impl<T: Config> Contract<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Create new contract and use a default account id as instantiator.
- fn new(
- module: WasmModule<T>,
- data: Vec<u8>,
- endowment: Endow,
- ) -> Result<Contract<T>, &'static str> {
- Self::with_index(0, module, data, endowment)
- }
-
- /// Create new contract and use an account id derived from the supplied index as instantiator.
- fn with_index(
- index: u32,
- module: WasmModule<T>,
- data: Vec<u8>,
- endowment: Endow,
- ) -> Result<Contract<T>, &'static str> {
- Self::with_caller(account("instantiator", index, 0), module, data, endowment)
- }
-
- /// Create new contract and use the supplied `caller` as instantiator.
- fn with_caller(
- caller: T::AccountId,
- module: WasmModule<T>,
- data: Vec<u8>,
- endowment: Endow,
- ) -> Result<Contract<T>, &'static str> {
- let (storage_size, endowment) = match endowment {
- Endow::CollectRent => {
- // storage_size cannot be zero because otherwise a contract that is just above
- // the subsistence threshold does not pay rent given a large enough subsistence
- // threshold. But we need rent payments to occur in order to benchmark for worst cases.
- let storage_size = u32::max_value() / 10;
-
- // Endowment should be large but not as large to inhibit rent payments.
- // Balance will only cover half the storage
- let endowment = T::DepositPerStorageByte::get()
- .saturating_mul(<BalanceOf<T>>::from(storage_size) / 2u32.into())
- .saturating_add(T::DepositPerContract::get());
-
- (storage_size, endowment)
- }
- Endow::Max => (0u32.into(), Endow::max::<T>()),
- };
- T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
- let salt = vec![0xff];
- let addr = Contracts::<T>::contract_address(&caller, &module.hash, &salt);
-
- // The default block number is zero. The benchmarking system bumps the block number
- // to one for the benchmarking closure when it is set to zero. In order to prevent this
- // undesired implicit bump (which messes with rent collection), we do the bump ourselves
- // in the setup closure so that both the instantiate and subsequent call are run with the
- // same block number.
- System::<T>::set_block_number(1u32.into());
-
- Contracts::<T>::store_code_raw(module.code)?;
- Contracts::<T>::instantiate(
- RawOrigin::Signed(caller.clone()).into(),
- endowment,
- Weight::max_value(),
- module.hash,
- data,
- salt,
- )?;
-
- let result = Contract {
- caller,
- account_id: addr.clone(),
- addr: T::Lookup::unlookup(addr),
- endowment,
- code_hash: module.hash.clone(),
- };
-
- let mut contract = result.alive_info()?;
- contract.storage_size = storage_size;
- ContractInfoOf::<T>::insert(&result.account_id, ContractInfo::Alive(contract));
-
- Ok(result)
- }
-
- /// Store the supplied storage items into this contracts storage.
- fn store(&self, items: &Vec<(StorageKey, Vec<u8>)>) -> Result<(), &'static str> {
- let info = self.alive_info()?;
- for item in items {
- Storage::<T>::write(
- &self.account_id,
- &info.trie_id,
- &item.0,
- Some(item.1.clone()),
- )
- .map_err(|_| "Failed to write storage to restoration dest")?;
- }
- Ok(())
- }
-
- /// Get the `AliveContractInfo` of the `addr` or an error if it is no longer alive.
- fn address_alive_info(addr: &T::AccountId) -> Result<AliveContractInfo<T>, &'static str> {
- ContractInfoOf::<T>::get(addr)
- .and_then(|c| c.get_alive())
- .ok_or("Expected contract to be alive at this point.")
- }
-
- /// Get the `AliveContractInfo` of this contract or an error if it is no longer alive.
- fn alive_info(&self) -> Result<AliveContractInfo<T>, &'static str> {
- Self::address_alive_info(&self.account_id)
- }
-
- /// Return an error if this contract is no tombstone.
- fn ensure_tombstone(&self) -> Result<(), &'static str> {
- ContractInfoOf::<T>::get(&self.account_id)
- .and_then(|c| c.get_tombstone())
- .ok_or("Expected contract to be a tombstone at this point.")
- .map(|_| ())
- }
-
- /// Get the block number when this contract will be evicted. Returns an error when
- /// the rent collection won't happen because the contract has to much endowment.
- fn eviction_at(&self) -> Result<T::BlockNumber, &'static str> {
- let projection = Rent::<T, PrefabWasmModule<T>>::compute_projection(&self.account_id)
- .map_err(|_| "Invalid acc for rent")?;
- match projection {
- RentProjection::EvictionAt(at) => Ok(at),
- _ => Err("Account does not pay rent.")?,
- }
- }
-}
-
-/// A `Contract` that contains some storage items.
-///
-/// This is used to benchmark contract destruction and resurection. Those operations'
-/// weight depend on the amount of storage accumulated.
-struct ContractWithStorage<T: Config> {
- /// The contract that was evicted.
- contract: Contract<T>,
- /// The storage the contract held when it was avicted.
- storage: Vec<(StorageKey, Vec<u8>)>,
-}
-
-impl<T: Config> ContractWithStorage<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Same as [`Self::with_code`] but with dummy contract code.
- fn new(stor_num: u32, stor_size: u32) -> Result<Self, &'static str> {
- Self::with_code(WasmModule::dummy(), stor_num, stor_size)
- }
-
- /// Create and evict a new contract with the supplied storage item count and size each.
- fn with_code(code: WasmModule<T>, stor_num: u32, stor_size: u32) -> Result<Self, &'static str> {
- let contract = Contract::<T>::new(code, vec![], Endow::CollectRent)?;
- let storage_items = create_storage::<T>(stor_num, stor_size)?;
- contract.store(&storage_items)?;
- Ok(Self {
- contract,
- storage: storage_items,
- })
- }
-
- /// Increase the system block number so that this contract is eligible for eviction.
- fn set_block_num_for_eviction(&self) -> Result<(), &'static str> {
- System::<T>::set_block_number(
- self.contract.eviction_at()? + T::SignedClaimHandicap::get() + 5u32.into(),
- );
- Ok(())
- }
-
- /// Evict this contract.
- fn evict(&mut self) -> Result<(), &'static str> {
- self.set_block_num_for_eviction()?;
- Rent::<T, PrefabWasmModule<T>>::try_eviction(&self.contract.account_id, Zero::zero())?;
- self.contract.ensure_tombstone()
- }
-}
-
-/// Generate `stor_num` storage items. Each has the size `stor_size`.
-fn create_storage<T: Config>(
- stor_num: u32,
- stor_size: u32,
-) -> Result<Vec<(StorageKey, Vec<u8>)>, &'static str> {
- (0..stor_num)
- .map(|i| {
- let hash = T::Hashing::hash_of(&i)
- .as_ref()
- .try_into()
- .map_err(|_| "Hash too big for storage key")?;
- Ok((hash, vec![42u8; stor_size as usize]))
- })
- .collect::<Result<Vec<_>, &'static str>>()
-}
-
-/// The funding that each account that either calls or instantiates contracts is funded with.
-fn caller_funding<T: Config>() -> BalanceOf<T> {
- BalanceOf::<T>::max_value() / 2u32.into()
-}
-
-benchmarks! {
- where_clause { where
- T::AccountId: UncheckedFrom<T::Hash>,
- T::AccountId: AsRef<[u8]>,
- }
-
- // The base weight without any actual work performed apart from the setup costs.
- on_initialize {}: {
- Storage::<T>::process_deletion_queue_batch(Weight::max_value())
- }
-
- on_initialize_per_trie_key {
- let k in 0..1024;
- let instance = ContractWithStorage::<T>::new(k, T::MaxValueSize::get())?;
- Storage::<T>::queue_trie_for_deletion(&instance.contract.alive_info()?)?;
- }: {
- Storage::<T>::process_deletion_queue_batch(Weight::max_value())
- }
-
- on_initialize_per_queue_item {
- let q in 0..1024.min(T::DeletionQueueDepth::get());
- for i in 0 .. q {
- let instance = Contract::<T>::with_index(i, WasmModule::dummy(), vec![], Endow::Max)?;
- Storage::<T>::queue_trie_for_deletion(&instance.alive_info()?)?;
- ContractInfoOf::<T>::remove(instance.account_id);
- }
- }: {
- Storage::<T>::process_deletion_queue_batch(Weight::max_value())
- }
-
- // This benchmarks the additional weight that is charged when a contract is executed the
- // first time after a new schedule was deployed: For every new schedule a contract needs
- // to re-run the instrumentation once.
- instrument {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
- Contracts::<T>::store_code_raw(code)?;
- let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
- let schedule = <CurrentSchedule<T>>::get();
- }: {
- Contracts::<T>::reinstrument_module(&mut module, &schedule)?;
- }
-
- // This extrinsic is pretty much constant as it is only a simple setter.
- update_schedule {
- let schedule = Schedule {
- version: 1,
- .. Default::default()
- };
- }: _(RawOrigin::Root, schedule)
-
- // This constructs a contract that is maximal expensive to instrument.
- // It creates a maximum number of metering blocks per byte.
- // The size of the salt influences the runtime because is is hashed in order to
- // determine the contract address.
- // `c`: Size of the code in kilobytes.
- // `s`: Size of the salt in kilobytes.
- //
- // # Note
- //
- // We cannot let `c` grow to the maximum code size because the code is not allowed
- // to be larger than the maximum size **after instrumentation**.
- instantiate_with_code {
- let c in 0 .. Perbill::from_percent(50).mul_ceil(T::MaxCodeSize::get() / 1024);
- let s in 0 .. code::max_pages::<T>() * 64;
- let salt = vec![42u8; (s * 1024) as usize];
- let endowment = caller_funding::<T>() / 3u32.into();
- let caller = whitelisted_caller();
- T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
- let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
- let origin = RawOrigin::Signed(caller.clone());
- let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
- }: _(origin, endowment, Weight::max_value(), code, vec![], salt)
- verify {
- // endowment was removed from the caller
- assert_eq!(T::Currency::free_balance(&caller), caller_funding::<T>() - endowment);
- // contract has the full endowment because no rent collection happended
- assert_eq!(T::Currency::free_balance(&addr), endowment);
- // instantiate should leave a alive contract
- Contract::<T>::address_alive_info(&addr)?;
- }
-
- // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate.
- // `c`: Size of the code in kilobytes.
- // `s`: Size of the salt in kilobytes.
- instantiate {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let s in 0 .. code::max_pages::<T>() * 64;
- let salt = vec![42u8; (s * 1024) as usize];
- let endowment = caller_funding::<T>() / 3u32.into();
- let caller = whitelisted_caller();
- T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
- let WasmModule { code, hash, .. } = WasmModule::<T>::dummy_with_bytes(c * 1024);
- let origin = RawOrigin::Signed(caller.clone());
- let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
- Contracts::<T>::store_code_raw(code)?;
- }: _(origin, endowment, Weight::max_value(), hash, vec![], salt)
- verify {
- // endowment was removed from the caller
- assert_eq!(T::Currency::free_balance(&caller), caller_funding::<T>() - endowment);
- // contract has the full endowment because no rent collection happended
- assert_eq!(T::Currency::free_balance(&addr), endowment);
- // instantiate should leave a alive contract
- Contract::<T>::address_alive_info(&addr)?;
- }
-
- // We just call a dummy contract to measure to overhead of the call extrinsic.
- // The size of the data has no influence on the costs of this extrinsic as long as the contract
- // won't call `seal_input` in its constructor to copy the data to contract memory.
- // The dummy contract used here does not do this. The costs for the data copy is billed as
- // part of `seal_input`.
- // `c`: Size of the code in kilobytes.
- call {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let data = vec![42u8; 1024];
- let instance = Contract::<T>::with_caller(
- whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
- )?;
- let value = T::Currency::minimum_balance() * 100u32.into();
- let origin = RawOrigin::Signed(instance.caller.clone());
- let callee = instance.addr.clone();
-
- // trigger rent collection for worst case performance of call
- System::<T>::set_block_number(instance.eviction_at()? - 5u32.into());
- let before = T::Currency::free_balance(&instance.account_id);
- }: _(origin, callee, value, Weight::max_value(), data)
- verify {
- // endowment and value transfered via call should be removed from the caller
- assert_eq!(
- T::Currency::free_balance(&instance.caller),
- caller_funding::<T>() - instance.endowment - value,
- );
- // rent should have lowered the amount of balance of the contract
- assert!(T::Currency::free_balance(&instance.account_id) < before + value);
- // but it should not have been evicted by the rent collection
- instance.alive_info()?;
- }
-
- // We benchmark the costs for sucessfully evicting an empty contract.
- // The actual costs are depending on how many storage items the evicted contract
- // does have. However, those costs are not to be payed by the sender but
- // will be distributed over multiple blocks using a scheduler. Otherwise there is
- // no incentive to remove large contracts when the removal is more expensive than
- // the reward for removing them.
- // `c`: Size of the code of the contract that should be evicted.
- claim_surcharge {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let instance = Contract::<T>::with_caller(
- whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
- )?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- let account_id = instance.account_id.clone();
-
- // instantiate should leave us with an alive contract
- instance.alive_info()?;
-
- // generate enough rent so that the contract is evicted
- System::<T>::set_block_number(
- instance.eviction_at()? + T::SignedClaimHandicap::get() + 5u32.into()
- );
- }: _(origin, account_id, None)
- verify {
- // the claim surcharge should have evicted the contract
- instance.ensure_tombstone()?;
-
- // the caller should get the reward for being a good snitch
- // this is capped by the maximum amount of rent payed. So we only now that it should
- // have increased by at most the surcharge reward.
- assert!(
- T::Currency::free_balance(&instance.caller) >
- caller_funding::<T>() - instance.endowment
- );
- assert!(
- T::Currency::free_balance(&instance.caller) <=
- caller_funding::<T>() - instance.endowment + <T as Config>::SurchargeReward::get(),
- );
- }
-
- seal_caller {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_caller", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_address {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_address", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_gas_left {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_gas_left", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_balance {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_balance", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_value_transferred {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_value_transferred", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_minimum_balance {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_minimum_balance", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_tombstone_deposit {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_tombstone_deposit", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_rent_allowance {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_rent_allowance", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_block_number {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_block_number", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_now {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_now", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_rent_params {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::getter(
- "seal_rent_params", r * API_BENCHMARK_BATCH_SIZE
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_weight_to_fee {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let pages = code::max_pages::<T>();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_weight_to_fee",
- params: vec![ValueType::I64, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![DataSegment {
- offset: 0,
- value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(),
- }],
- call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I64Const(500_000),
- Instruction::I32Const(4),
- Instruction::I32Const(0),
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_gas {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let code = WasmModule::<T>::from(ModuleDefinition {
- imported_functions: vec![ImportedFunction {
- name: "gas",
- params: vec![ValueType::I32],
- return_type: None,
- }],
- call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I32Const(42),
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
-
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // We cannot call seal_input multiple times. Therefore our weight determination is not
- // as precise as with other APIs. Because this function can only be called once per
- // contract it cannot be used for Dos.
- seal_input {
- let r in 0 .. 1;
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_input",
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: 0u32.to_le_bytes().to_vec(),
- },
- ],
- call_body: Some(body::repeated(r, &[
- Instruction::I32Const(4), // ptr where to store output
- Instruction::I32Const(0), // ptr to length
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_input_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let pages = code::max_pages::<T>();
- let buffer_size = pages * 64 * 1024 - 4;
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_input",
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: buffer_size.to_le_bytes().to_vec(),
- },
- ],
- call_body: Some(body::plain(vec![
- Instruction::I32Const(4), // ptr where to store output
- Instruction::I32Const(0), // ptr to length
- Instruction::Call(0),
- Instruction::End,
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let data = vec![42u8; (n * 1024).min(buffer_size) as usize];
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), data)
-
- // The same argument as for `seal_input` is true here.
- seal_return {
- let r in 0 .. 1;
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_return",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- call_body: Some(body::repeated(r, &[
- Instruction::I32Const(0), // flags
- Instruction::I32Const(0), // data_ptr
- Instruction::I32Const(0), // data_len
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_return_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_return",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- call_body: Some(body::plain(vec![
- Instruction::I32Const(0), // flags
- Instruction::I32Const(0), // data_ptr
- Instruction::I32Const((n * 1024) as i32), // data_len
- Instruction::Call(0),
- Instruction::End,
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // The same argument as for `seal_input` is true here.
- seal_terminate {
- let r in 0 .. 1;
- let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
- let beneficiary_bytes = beneficiary.encode();
- let beneficiary_len = beneficiary_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_terminate",
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: beneficiary_bytes,
- },
- ],
- call_body: Some(body::repeated(r, &[
- Instruction::I32Const(0), // beneficiary_ptr
- Instruction::I32Const(beneficiary_len as i32), // beneficiary_len
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into());
- assert_eq!(T::Currency::total_balance(&instance.account_id), Endow::max::<T>());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
- verify {
- if r > 0 {
- assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into());
- assert_eq!(T::Currency::total_balance(&beneficiary), Endow::max::<T>());
- }
- }
-
- seal_terminate_per_code_kb {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
- let beneficiary_bytes = beneficiary.encode();
- let beneficiary_len = beneficiary_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_terminate",
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: beneficiary_bytes,
- },
- ],
- call_body: Some(body::repeated(1, &[
- Instruction::I32Const(0), // beneficiary_ptr
- Instruction::I32Const(beneficiary_len as i32), // beneficiary_len
- Instruction::Call(0),
- ])),
- dummy_section: c * 1024,
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into());
- assert_eq!(T::Currency::total_balance(&instance.account_id), Endow::max::<T>());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
- verify {
- assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into());
- assert_eq!(T::Currency::total_balance(&beneficiary), Endow::max::<T>());
- }
-
- seal_restore_to {
- let r in 0 .. 1;
-
- // Restore just moves the trie id from origin to destination and therefore
- // does not depend on the size of the destination contract. However, to not
- // trigger any edge case we won't use an empty contract as destination.
- let mut tombstone = ContractWithStorage::<T>::new(10, T::MaxValueSize::get())?;
- tombstone.evict()?;
-
- let dest = tombstone.contract.account_id.encode();
- let dest_len = dest.len();
- let code_hash = tombstone.contract.code_hash.encode();
- let code_hash_len = code_hash.len();
- let rent_allowance = BalanceOf::<T>::max_value().encode();
- let rent_allowance_len = rent_allowance.len();
-
- let dest_offset = 0;
- let code_hash_offset = dest_offset + dest_len;
- let rent_allowance_offset = code_hash_offset + code_hash_len;
-
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_restore_to",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: dest_offset as u32,
- value: dest,
- },
- DataSegment {
- offset: code_hash_offset as u32,
- value: code_hash,
- },
- DataSegment {
- offset: rent_allowance_offset as u32,
- value: rent_allowance,
- },
- ],
- call_body: Some(body::repeated(r, &[
- Instruction::I32Const(dest_offset as i32),
- Instruction::I32Const(dest_len as i32),
- Instruction::I32Const(code_hash_offset as i32),
- Instruction::I32Const(code_hash_len as i32),
- Instruction::I32Const(rent_allowance_offset as i32),
- Instruction::I32Const(rent_allowance_len as i32),
- Instruction::I32Const(0), // delta_ptr
- Instruction::I32Const(0), // delta_count
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
-
- let instance = Contract::<T>::with_caller(
- account("origin", 0, 0), code, vec![], Endow::Max
- )?;
- instance.store(&tombstone.storage)?;
- System::<T>::set_block_number(System::<T>::block_number() + 1u32.into());
-
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
- verify {
- if r > 0 {
- tombstone.contract.alive_info()?;
- }
- }
-
- // `c`: Code size of caller contract
- // `t`: Code size of tombstone contract
- // `d`: Number of supplied delta keys
- seal_restore_to_per_code_kb_delta {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let t in 0 .. T::MaxCodeSize::get() / 1024;
- let d in 0 .. API_BENCHMARK_BATCHES;
- let mut tombstone = ContractWithStorage::<T>::with_code(
- WasmModule::<T>::dummy_with_bytes(t * 1024), 0, 0
- )?;
- tombstone.evict()?;
- let delta = create_storage::<T>(d * API_BENCHMARK_BATCH_SIZE, T::MaxValueSize::get())?;
-
- let dest = tombstone.contract.account_id.encode();
- let dest_len = dest.len();
- let code_hash = tombstone.contract.code_hash.encode();
- let code_hash_len = code_hash.len();
- let rent_allowance = BalanceOf::<T>::max_value().encode();
- let rent_allowance_len = rent_allowance.len();
- let delta_keys = delta.iter().flat_map(|(key, _)| key).cloned().collect::<Vec<_>>();
-
- let dest_offset = 0;
- let code_hash_offset = dest_offset + dest_len;
- let rent_allowance_offset = code_hash_offset + code_hash_len;
- let delta_keys_offset = rent_allowance_offset + rent_allowance_len;
-
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_restore_to",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: dest_offset as u32,
- value: dest,
- },
- DataSegment {
- offset: code_hash_offset as u32,
- value: code_hash,
- },
- DataSegment {
- offset: rent_allowance_offset as u32,
- value: rent_allowance,
- },
- DataSegment {
- offset: delta_keys_offset as u32,
- value: delta_keys,
- },
- ],
- call_body: Some(body::plain(vec![
- Instruction::I32Const(dest_offset as i32),
- Instruction::I32Const(dest_len as i32),
- Instruction::I32Const(code_hash_offset as i32),
- Instruction::I32Const(code_hash_len as i32),
- Instruction::I32Const(rent_allowance_offset as i32),
- Instruction::I32Const(rent_allowance_len as i32),
- Instruction::I32Const(delta_keys_offset as i32), // delta_ptr
- Instruction::I32Const(delta.len() as i32), // delta_count
- Instruction::Call(0),
- Instruction::End,
- ])),
- dummy_section: c * 1024,
- .. Default::default()
- });
-
- let instance = Contract::<T>::with_caller(
- account("origin", 0, 0), code, vec![], Endow::Max
- )?;
- instance.store(&tombstone.storage)?;
- instance.store(&delta)?;
- System::<T>::set_block_number(System::<T>::block_number() + 1u32.into());
-
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
- verify {
- tombstone.contract.alive_info()?;
- }
-
- // We benchmark only for the maximum subject length. We assume that this is some lowish
- // number (< 1 KB). Therefore we are not overcharging too much in case a smaller subject is
- // used.
- seal_random {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let pages = code::max_pages::<T>();
- let subject_len = <CurrentSchedule<T>>::get().limits.subject_len;
- assert!(subject_len < 1024);
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_random",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: (pages * 64 * 1024 - subject_len - 4).to_le_bytes().to_vec(),
- },
- ],
- call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I32Const(4), // subject_ptr
- Instruction::I32Const(subject_len as i32), // subject_len
- Instruction::I32Const((subject_len + 4) as i32), // out_ptr
- Instruction::I32Const(0), // out_len_ptr
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Overhead of calling the function without any topic.
- // We benchmark for the worst case (largest event).
- seal_deposit_event {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_deposit_event",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I32Const(0), // topics_ptr
- Instruction::I32Const(0), // topics_len
- Instruction::I32Const(0), // data_ptr
- Instruction::I32Const(0), // data_len
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Benchmark the overhead that topics generate.
- // `t`: Number of topics
- // `n`: Size of event payload in kb
- seal_deposit_event_per_topic_and_kb {
- let t in 0 .. <CurrentSchedule<T>>::get().limits.event_topics;
- let n in 0 .. T::MaxValueSize::get() / 1024;
- let mut topics = (0..API_BENCHMARK_BATCH_SIZE)
- .map(|n| (n * t..n * t + t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode())
- .peekable();
- let topics_len = topics.peek().map(|i| i.len()).unwrap_or(0);
- let topics = topics.flatten().collect();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_deposit_event",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: topics,
- },
- ],
- call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
- Counter(0, topics_len as u32), // topics_ptr
- Regular(Instruction::I32Const(topics_len as i32)), // topics_len
- Regular(Instruction::I32Const(0)), // data_ptr
- Regular(Instruction::I32Const((n * 1024) as i32)), // data_len
- Regular(Instruction::Call(0)),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_set_rent_allowance {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let allowance = caller_funding::<T>().encode();
- let allowance_len = allowance.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }),
- imported_functions: vec![ImportedFunction {
- name: "seal_set_rent_allowance",
- params: vec![ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: allowance,
- },
- ],
- call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I32Const(0), // value_ptr
- Instruction::I32Const(allowance_len as i32), // value_len
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Only the overhead of calling the function itself with minimal arguments.
- // The contract is a bit more complex because I needs to use different keys in order
- // to generate unique storage accesses. However, it is still dominated by the storage
- // accesses.
- seal_set_storage {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE)
- .flat_map(|n| T::Hashing::hash_of(&n).as_ref().to_vec())
- .collect::<Vec<_>>();
- let key_len = sp_std::mem::size_of::<<T::Hashing as sp_runtime::traits::Hash>::Output>();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_set_storage",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: keys,
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(0, key_len as u32), // key_ptr
- Regular(Instruction::I32Const(0)), // value_ptr
- Regular(Instruction::I32Const(0)), // value_len
- Regular(Instruction::Call(0)),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_set_storage_per_kb {
- let n in 0 .. T::MaxValueSize::get() / 1024;
- let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
- let key_len = key.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_set_storage",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: key,
- },
- ],
- call_body: Some(body::repeated(API_BENCHMARK_BATCH_SIZE, &[
- Instruction::I32Const(0), // key_ptr
- Instruction::I32Const(0), // value_ptr
- Instruction::I32Const((n * 1024) as i32), // value_len
- Instruction::Call(0),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Similar to seal_set_storage. However, we store all the keys that we are about to
- // delete beforehand in order to prevent any optimizations that could occur when
- // deleting a non existing key.
- seal_clear_storage {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE)
- .map(|n| T::Hashing::hash_of(&n).as_ref().to_vec())
- .collect::<Vec<_>>();
- let key_bytes = keys.iter().flatten().cloned().collect::<Vec<_>>();
- let key_len = sp_std::mem::size_of::<<T::Hashing as sp_runtime::traits::Hash>::Output>();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_clear_storage",
- params: vec![ValueType::I32],
- return_type: None,
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: key_bytes,
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(0, key_len as u32),
- Regular(Instruction::Call(0)),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let trie_id = instance.alive_info()?.trie_id;
- for key in keys {
- Storage::<T>::write(
- &instance.account_id,
- &trie_id,
- key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
- Some(vec![42; T::MaxValueSize::get() as usize])
- )
- .map_err(|_| "Failed to write to storage during setup.")?;
- }
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // We make sure that all storage accesses are to unique keys.
- seal_get_storage {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let keys = (0 .. r * API_BENCHMARK_BATCH_SIZE)
- .map(|n| T::Hashing::hash_of(&n).as_ref().to_vec())
- .collect::<Vec<_>>();
- let key_len = sp_std::mem::size_of::<<T::Hashing as sp_runtime::traits::Hash>::Output>();
- let key_bytes = keys.iter().flatten().cloned().collect::<Vec<_>>();
- let key_bytes_len = key_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_get_storage",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: key_bytes,
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(0, key_len as u32), // key_ptr
- Regular(Instruction::I32Const((key_bytes_len + 4) as i32)), // out_ptr
- Regular(Instruction::I32Const(key_bytes_len as i32)), // out_len_ptr
- Regular(Instruction::Call(0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let trie_id = instance.alive_info()?.trie_id;
- for key in keys {
- Storage::<T>::write(
- &instance.account_id,
- &trie_id,
- key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
- Some(vec![])
- )
- .map_err(|_| "Failed to write to storage during setup.")?;
- }
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_get_storage_per_kb {
- let n in 0 .. T::MaxValueSize::get() / 1024;
- let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
- let key_len = key.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_get_storage",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: key.clone(),
- },
- DataSegment {
- offset: key_len as u32,
- value: T::MaxValueSize::get().to_le_bytes().into(),
- },
- ],
- call_body: Some(body::repeated(API_BENCHMARK_BATCH_SIZE, &[
- // call at key_ptr
- Instruction::I32Const(0), // key_ptr
- Instruction::I32Const((key_len + 4) as i32), // out_ptr
- Instruction::I32Const(key_len as i32), // out_len_ptr
- Instruction::Call(0),
- Instruction::Drop,
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let trie_id = instance.alive_info()?.trie_id;
- Storage::<T>::write(
- &instance.account_id,
- &trie_id,
- key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
- Some(vec![42u8; (n * 1024) as usize])
- )
- .map_err(|_| "Failed to write to storage during setup.")?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // We transfer to unique accounts.
- seal_transfer {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let accounts = (0..r * API_BENCHMARK_BATCH_SIZE)
- .map(|i| account::<T::AccountId>("receiver", i, 0))
- .collect::<Vec<_>>();
- let account_len = accounts.get(0).map(|i| i.encode().len()).unwrap_or(0);
- let account_bytes = accounts.iter().flat_map(|x| x.encode()).collect();
- let value = Contracts::<T>::subsistence_threshold();
- assert!(value > 0u32.into());
- let value_bytes = value.encode();
- let value_len = value_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_transfer",
- params: vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: value_bytes,
- },
- DataSegment {
- offset: value_len as u32,
- value: account_bytes,
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(value_len as u32, account_len as u32), // account_ptr
- Regular(Instruction::I32Const(account_len as i32)), // account_len
- Regular(Instruction::I32Const(0)), // value_ptr
- Regular(Instruction::I32Const(value_len as i32)), // value_len
- Regular(Instruction::Call(0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- for account in &accounts {
- assert_eq!(T::Currency::total_balance(account), 0u32.into());
- }
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
- verify {
- for account in &accounts {
- assert_eq!(T::Currency::total_balance(account), value);
- }
- }
-
- // We call unique accounts.
- seal_call {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let dummy_code = WasmModule::<T>::dummy_with_bytes(0);
- let callees = (0..r * API_BENCHMARK_BATCH_SIZE)
- .map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![], Endow::Max))
- .collect::<Result<Vec<_>, _>>()?;
- let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0);
- let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect();
- let value: BalanceOf<T> = 0u32.into();
- let value_bytes = value.encode();
- let value_len = value_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_call",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I64,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: value_bytes,
- },
- DataSegment {
- offset: value_len as u32,
- value: callee_bytes,
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(value_len as u32, callee_len as u32), // callee_ptr
- Regular(Instruction::I32Const(callee_len as i32)), // callee_len
- Regular(Instruction::I64Const(0)), // gas
- Regular(Instruction::I32Const(0)), // value_ptr
- Regular(Instruction::I32Const(value_len as i32)), // value_len
- Regular(Instruction::I32Const(0)), // input_data_ptr
- Regular(Instruction::I32Const(0)), // input_data_len
- Regular(Instruction::I32Const(u32::max_value() as i32)), // output_ptr
- Regular(Instruction::I32Const(0)), // output_len_ptr
- Regular(Instruction::Call(0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- seal_call_per_code_transfer_input_output_kb {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let t in 0 .. 1;
- let i in 0 .. code::max_pages::<T>() * 64;
- let o in 0 .. (code::max_pages::<T>() - 1) * 64;
- let callee_code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_return",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: None,
- }],
- call_body: Some(body::plain(vec![
- Instruction::I32Const(0), // flags
- Instruction::I32Const(0), // data_ptr
- Instruction::I32Const((o * 1024) as i32), // data_len
- Instruction::Call(0),
- Instruction::End,
- ])),
- dummy_section: c * 1024,
- .. Default::default()
- });
- let callees = (0..API_BENCHMARK_BATCH_SIZE)
- .map(|i| Contract::with_index(i + 1, callee_code.clone(), vec![], Endow::Max))
- .collect::<Result<Vec<_>, _>>()?;
- let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0);
- let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect::<Vec<_>>();
- let callees_len = callee_bytes.len();
- let value: BalanceOf<T> = t.into();
- let value_bytes = value.encode();
- let value_len = value_bytes.len();
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_call",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I64,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: 0,
- value: value_bytes,
- },
- DataSegment {
- offset: value_len as u32,
- value: callee_bytes,
- },
- DataSegment {
- offset: (value_len + callees_len) as u32,
- value: (o * 1024).to_le_bytes().into(),
- },
- ],
- call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
- Counter(value_len as u32, callee_len as u32), // callee_ptr
- Regular(Instruction::I32Const(callee_len as i32)), // callee_len
- Regular(Instruction::I64Const(0)), // gas
- Regular(Instruction::I32Const(0)), // value_ptr
- Regular(Instruction::I32Const(value_len as i32)), // value_len
- Regular(Instruction::I32Const(0)), // input_data_ptr
- Regular(Instruction::I32Const((i * 1024) as i32)), // input_data_len
- Regular(Instruction::I32Const((value_len + callees_len + 4) as i32)), // output_ptr
- Regular(Instruction::I32Const((value_len + callees_len) as i32)), // output_len_ptr
- Regular(Instruction::Call(0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // We assume that every instantiate sends at least the subsistence amount.
- seal_instantiate {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let hashes = (0..r * API_BENCHMARK_BATCH_SIZE)
- .map(|i| {
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- call_body: Some(body::plain(vec![
- // we need to add this in order to make contracts unique
- // so that they can be deployed from the same sender
- Instruction::I32Const(i as i32),
- Instruction::Drop,
- Instruction::End,
- ])),
- .. Default::default()
- });
- Contracts::<T>::store_code_raw(code.code)?;
- Ok(code.hash)
- })
- .collect::<Result<Vec<_>, &'static str>>()?;
- let hash_len = hashes.get(0).map(|x| x.encode().len()).unwrap_or(0);
- let hashes_bytes = hashes.iter().flat_map(|x| x.encode()).collect::<Vec<_>>();
- let hashes_len = hashes_bytes.len();
- let value = Endow::max::<T>() / (r * API_BENCHMARK_BATCH_SIZE + 2).into();
- assert!(value > 0u32.into());
- let value_bytes = value.encode();
- let value_len = value_bytes.len();
- let addr_len = sp_std::mem::size_of::<T::AccountId>();
-
- // offsets where to place static data in contract memory
- let value_offset = 0;
- let hashes_offset = value_offset + value_len;
- let addr_len_offset = hashes_offset + hashes_len;
- let addr_offset = addr_len_offset + addr_len;
-
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_instantiate",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I64,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: value_offset as u32,
- value: value_bytes,
- },
- DataSegment {
- offset: hashes_offset as u32,
- value: hashes_bytes,
- },
- DataSegment {
- offset: addr_len_offset as u32,
- value: addr_len.to_le_bytes().into(),
- },
- ],
- call_body: Some(body::repeated_dyn(r * API_BENCHMARK_BATCH_SIZE, vec![
- Counter(hashes_offset as u32, hash_len as u32), // code_hash_ptr
- Regular(Instruction::I32Const(hash_len as i32)), // code_hash_len
- Regular(Instruction::I64Const(0)), // gas
- Regular(Instruction::I32Const(value_offset as i32)), // value_ptr
- Regular(Instruction::I32Const(value_len as i32)), // value_len
- Regular(Instruction::I32Const(0)), // input_data_ptr
- Regular(Instruction::I32Const(0)), // input_data_len
- Regular(Instruction::I32Const(addr_offset as i32)), // address_ptr
- Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr
- Regular(Instruction::I32Const(u32::max_value() as i32)), // output_ptr
- Regular(Instruction::I32Const(0)), // output_len_ptr
- Regular(Instruction::I32Const(0)), // salt_ptr
- Regular(Instruction::I32Const(0)), // salt_ptr_len
- Regular(Instruction::Call(0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- let callee = instance.addr.clone();
- let addresses = hashes
- .iter()
- .map(|hash| Contracts::<T>::contract_address(
- &instance.account_id, hash, &[],
- ))
- .collect::<Vec<_>>();
-
- for addr in &addresses {
- if let Some(_) = ContractInfoOf::<T>::get(&addr) {
- return Err("Expected that contract does not exist at this point.");
- }
- }
- }: call(origin, callee, 0u32.into(), Weight::max_value(), vec![])
- verify {
- for addr in &addresses {
- ContractInfoOf::<T>::get(&addr).and_then(|c| c.get_alive())
- .ok_or_else(|| "Contract should have been instantiated")?;
- }
- }
-
- seal_instantiate_per_code_input_output_salt_kb {
- let c in 0 .. T::MaxCodeSize::get() / 1024;
- let i in 0 .. (code::max_pages::<T>() - 1) * 64;
- let o in 0 .. (code::max_pages::<T>() - 1) * 64;
- let s in 0 .. (code::max_pages::<T>() - 1) * 64;
- let callee_code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_return",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: None,
- }],
- deploy_body: Some(body::plain(vec![
- Instruction::I32Const(0), // flags
- Instruction::I32Const(0), // data_ptr
- Instruction::I32Const((o * 1024) as i32), // data_len
- Instruction::Call(0),
- Instruction::End,
- ])),
- dummy_section: c * 1024,
- .. Default::default()
- });
- let hash = callee_code.hash.clone();
- let hash_bytes = callee_code.hash.encode();
- let hash_len = hash_bytes.len();
- Contracts::<T>::store_code_raw(callee_code.code)?;
- let inputs = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::<Vec<_>>();
- let input_len = inputs.get(0).map(|x| x.len()).unwrap_or(0);
- let input_bytes = inputs.iter().cloned().flatten().collect::<Vec<_>>();
- let inputs_len = input_bytes.len();
- let value = Endow::max::<T>() / (API_BENCHMARK_BATCH_SIZE + 2).into();
- assert!(value > 0u32.into());
- let value_bytes = value.encode();
- let value_len = value_bytes.len();
- let addr_len = sp_std::mem::size_of::<T::AccountId>();
-
- // offsets where to place static data in contract memory
- let input_offset = 0;
- let value_offset = inputs_len;
- let hash_offset = value_offset + value_len;
- let addr_len_offset = hash_offset + hash_len;
- let output_len_offset = addr_len_offset + 4;
- let output_offset = output_len_offset + 4;
-
- let code = WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- imported_functions: vec![ImportedFunction {
- name: "seal_instantiate",
- params: vec![
- ValueType::I32,
- ValueType::I32,
- ValueType::I64,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ValueType::I32,
- ],
- return_type: Some(ValueType::I32),
- }],
- data_segments: vec![
- DataSegment {
- offset: input_offset as u32,
- value: input_bytes,
- },
- DataSegment {
- offset: value_offset as u32,
- value: value_bytes,
- },
- DataSegment {
- offset: hash_offset as u32,
- value: hash_bytes,
- },
- DataSegment {
- offset: addr_len_offset as u32,
- value: (addr_len as u32).to_le_bytes().into(),
- },
- DataSegment {
- offset: output_len_offset as u32,
- value: (o * 1024).to_le_bytes().into(),
- },
- ],
- call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
- Regular(Instruction::I32Const(hash_offset as i32)), // code_hash_ptr
- Regular(Instruction::I32Const(hash_len as i32)), // code_hash_len
- Regular(Instruction::I64Const(0)), // gas
- Regular(Instruction::I32Const(value_offset as i32)), // value_ptr
- Regular(Instruction::I32Const(value_len as i32)), // value_len
- Counter(input_offset as u32, input_len as u32), // input_data_ptr
- Regular(Instruction::I32Const((i * 1024).max(input_len as u32) as i32)), // input_data_len
- Regular(Instruction::I32Const((addr_len_offset + addr_len) as i32)), // address_ptr
- Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr
- Regular(Instruction::I32Const(output_offset as i32)), // output_ptr
- Regular(Instruction::I32Const(output_len_offset as i32)), // output_len_ptr
- Counter(input_offset as u32, input_len as u32), // salt_ptr
- Regular(Instruction::I32Const((s * 1024).max(input_len as u32) as i32)), // salt_len
- Regular(Instruction::Call(0)),
- Regular(Instruction::I32Eqz),
- Regular(Instruction::If(BlockType::NoResult)),
- Regular(Instruction::Nop),
- Regular(Instruction::Else),
- Regular(Instruction::Unreachable),
- Regular(Instruction::End),
- ])),
- .. Default::default()
- });
- let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Only the overhead of calling the function itself with minimal arguments.
- seal_hash_sha2_256 {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_sha2_256", r * API_BENCHMARK_BATCH_SIZE, 0,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // `n`: Input to hash in kilobytes
- seal_hash_sha2_256_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_sha2_256", API_BENCHMARK_BATCH_SIZE, n * 1024,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Only the overhead of calling the function itself with minimal arguments.
- seal_hash_keccak_256 {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_keccak_256", r * API_BENCHMARK_BATCH_SIZE, 0,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // `n`: Input to hash in kilobytes
- seal_hash_keccak_256_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_keccak_256", API_BENCHMARK_BATCH_SIZE, n * 1024,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Only the overhead of calling the function itself with minimal arguments.
- seal_hash_blake2_256 {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_blake2_256", r * API_BENCHMARK_BATCH_SIZE, 0,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // `n`: Input to hash in kilobytes
- seal_hash_blake2_256_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_blake2_256", API_BENCHMARK_BATCH_SIZE, n * 1024,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // Only the overhead of calling the function itself with minimal arguments.
- seal_hash_blake2_128 {
- let r in 0 .. API_BENCHMARK_BATCHES;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_blake2_128", r * API_BENCHMARK_BATCH_SIZE, 0,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // `n`: Input to hash in kilobytes
- seal_hash_blake2_128_per_kb {
- let n in 0 .. code::max_pages::<T>() * 64;
- let instance = Contract::<T>::new(WasmModule::hasher(
- "seal_hash_blake2_128", API_BENCHMARK_BATCH_SIZE, n * 1024,
- ), vec![], Endow::Max)?;
- let origin = RawOrigin::Signed(instance.caller.clone());
- }: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
-
- // We make the assumption that pushing a constant and dropping a value takes roughly
- // the same amount of time. We follow that `t.load` and `drop` both have the weight
- // of this benchmark / 2. We need to make this assumption because there is no way
- // to measure them on their own using a valid wasm module. We need their individual
- // values to derive the weight of individual instructions (by substraction) from
- // benchmarks that include those for parameter pushing and return type dropping.
- // We call the weight of `t.load` and `drop`: `w_param`.
- // The weight that would result from the respective benchmark we call: `w_bench`.
- //
- // w_i{32,64}const = w_drop = w_bench / 2
- instr_i64const {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_i{32,64}load = w_bench - 2 * w_param
- instr_i64load {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomUnaligned(0, code::max_pages::<T>() * 64 * 1024 - 8),
- Regular(Instruction::I64Load(3, 0)),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_i{32,64}store{...} = w_bench - 2 * w_param
- instr_i64store {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomUnaligned(0, code::max_pages::<T>() * 64 * 1024 - 8),
- RandomI64Repeated(1),
- Regular(Instruction::I64Store(3, 0)),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_select = w_bench - 4 * w_param
- instr_select {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(1),
- RandomI64Repeated(1),
- RandomI32(0, 2),
- Regular(Instruction::Select),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_if = w_bench - 3 * w_param
- instr_if {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI32(0, 2),
- Regular(Instruction::If(BlockType::Value(ValueType::I64))),
- RandomI64Repeated(1),
- Regular(Instruction::Else),
- RandomI64Repeated(1),
- Regular(Instruction::End),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_br = w_bench - 2 * w_param
- instr_br {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Br(1)),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_br_if = w_bench - 5 * w_param
- // The two additional pushes + drop are only executed 50% of the time.
- // Making it: 3 * w_param + (50% * 4 * w_param)
- instr_br_if {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- RandomI32(0, 2),
- Regular(Instruction::BrIf(1)),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_br_table = w_bench - 3 * w_param
- // 1 * w_param + 0.5 * 2 * w_param + 0.25 * 4 * w_param
- instr_br_table {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let table = Box::new(parity_wasm::elements::BrTableData {
- table: Box::new([0, 1, 2]),
- default: 1,
- });
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- RandomI32(0, 4),
- Regular(Instruction::BrTable(table)),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_br_table_per_entry = w_bench
- instr_br_table_per_entry {
- let e in 1 .. <CurrentSchedule<T>>::get().limits.br_table_size;
- let entry: Vec<u32> = [0, 1].iter()
- .cloned()
- .cycle()
- .take((e / 2) as usize).collect();
- let table = Box::new(parity_wasm::elements::BrTableData {
- table: entry.into_boxed_slice(),
- default: 0,
- });
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(INSTR_BENCHMARK_BATCH_SIZE, vec![
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- Regular(Instruction::Block(BlockType::NoResult)),
- RandomI32(0, (e + 1) as i32), // Make sure the default entry is also used
- Regular(Instruction::BrTable(table)),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- RandomI64Repeated(1),
- Regular(Instruction::Drop),
- Regular(Instruction::End),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_call = w_bench - 2 * w_param
- instr_call {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- // We need to make use of the stack here in order to trigger stack height
- // instrumentation.
- aux_body: Some(body::plain(vec![
- Instruction::I64Const(42),
- Instruction::Drop,
- Instruction::End,
- ])),
- call_body: Some(body::repeated(r * INSTR_BENCHMARK_BATCH_SIZE, &[
- Instruction::Call(2), // call aux
- ])),
- inject_stack_metering: true,
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_call_indrect = w_bench - 3 * w_param
- instr_call_indirect {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
- use self::code::TableSegment;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- // We need to make use of the stack here in order to trigger stack height
- // instrumentation.
- aux_body: Some(body::plain(vec![
- Instruction::I64Const(42),
- Instruction::Drop,
- Instruction::End,
- ])),
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI32(0, num_elements as i32),
- Regular(Instruction::CallIndirect(0, 0)), // we only have one sig: 0
- ])),
- inject_stack_metering: true,
- table: Some(TableSegment {
- num_elements,
- function_index: 2, // aux
- }),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_instr_call_indirect_per_param = w_bench - 1 * w_param
- // Calling a function indirectly causes it to go through a thunk function whose runtime
- // linearly depend on the amount of parameters to this function.
- // Please note that this is not necessary with a direct call.
- instr_call_indirect_per_param {
- let p in 0 .. <CurrentSchedule<T>>::get().limits.parameters;
- let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
- use self::code::TableSegment;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- // We need to make use of the stack here in order to trigger stack height
- // instrumentation.
- aux_body: Some(body::plain(vec![
- Instruction::I64Const(42),
- Instruction::Drop,
- Instruction::End,
- ])),
- aux_arg_num: p,
- call_body: Some(body::repeated_dyn(INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(p as usize),
- RandomI32(0, num_elements as i32),
- Regular(Instruction::CallIndirect(p.min(1), 0)), // aux signature: 1 or 0
- ])),
- inject_stack_metering: true,
- table: Some(TableSegment {
- num_elements,
- function_index: 2, // aux
- }),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_local_get = w_bench - 1 * w_param
- instr_local_get {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
- let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomGetLocal(0, max_locals),
- Regular(Instruction::Drop),
- ]);
- body::inject_locals(&mut call_body, max_locals);
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(call_body),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_local_set = w_bench - 1 * w_param
- instr_local_set {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
- let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(1),
- RandomSetLocal(0, max_locals),
- ]);
- body::inject_locals(&mut call_body, max_locals);
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(call_body),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_local_tee = w_bench - 2 * w_param
- instr_local_tee {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
- let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(1),
- RandomTeeLocal(0, max_locals),
- Regular(Instruction::Drop),
- ]);
- body::inject_locals(&mut call_body, max_locals);
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(call_body),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_global_get = w_bench - 1 * w_param
- instr_global_get {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let max_globals = <CurrentSchedule<T>>::get().limits.globals;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomGetGlobal(0, max_globals),
- Regular(Instruction::Drop),
- ])),
- num_globals: max_globals,
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_global_set = w_bench - 1 * w_param
- instr_global_set {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let max_globals = <CurrentSchedule<T>>::get().limits.globals;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI64Repeated(1),
- RandomSetGlobal(0, max_globals),
- ])),
- num_globals: max_globals,
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_memory_get = w_bench - 1 * w_param
- instr_memory_current {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory::max::<T>()),
- call_body: Some(body::repeated(r * INSTR_BENCHMARK_BATCH_SIZE, &[
- Instruction::CurrentMemory(0),
- Instruction::Drop
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // w_memory_grow = w_bench - 2 * w_param
- // We can only allow allocate as much memory as it is allowed in a a contract.
- // Therefore the repeat count is limited by the maximum memory any contract can have.
- // Using a contract with more memory will skew the benchmark because the runtime of grow
- // depends on how much memory is already allocated.
- instr_memory_grow {
- let r in 0 .. 1;
- let max_pages = ImportedMemory::max::<T>().max_pages;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- memory: Some(ImportedMemory {
- min_pages: 0,
- max_pages,
- }),
- call_body: Some(body::repeated(r * max_pages, &[
- Instruction::I32Const(1),
- Instruction::GrowMemory(0),
- Instruction::Drop,
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- // Unary numeric instructions.
- // All use w = w_bench - 2 * w_param.
-
- instr_i64clz {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::unary_instr(
- Instruction::I64Clz,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64ctz {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::unary_instr(
- Instruction::I64Ctz,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64popcnt {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::unary_instr(
- Instruction::I64Popcnt,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64eqz {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::unary_instr(
- Instruction::I64Eqz,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64extendsi32 {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI32Repeated(1),
- Regular(Instruction::I64ExtendSI32),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- instr_i64extendui32 {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
- call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
- RandomI32Repeated(1),
- Regular(Instruction::I64ExtendUI32),
- Regular(Instruction::Drop),
- ])),
- .. Default::default()
- }));
- }: {
- sbox.invoke();
- }
-
- instr_i32wrapi64 {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::unary_instr(
- Instruction::I32WrapI64,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- // Binary numeric instructions.
- // All use w = w_bench - 3 * w_param.
-
- instr_i64eq {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Eq,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64ne {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Ne,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64lts {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64LtS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64ltu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64LtU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64gts {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64GtS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64gtu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64GtU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64les {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64LeS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64leu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64LeU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64ges {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64GeS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64geu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64GeU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64add {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Add,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64sub {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Sub,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64mul {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Mul,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64divs {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64DivS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64divu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64DivU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64rems {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64RemS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64remu {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64RemU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64and {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64And,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64or {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Or,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64xor {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Xor,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64shl {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Shl,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64shrs {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64ShrS,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64shru {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64ShrU,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64rotl {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Rotl,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- instr_i64rotr {
- let r in 0 .. INSTR_BENCHMARK_BATCHES;
- let mut sbox = Sandbox::from(&WasmModule::<T>::binary_instr(
- Instruction::I64Rotr,
- r * INSTR_BENCHMARK_BATCH_SIZE,
- ));
- }: {
- sbox.invoke();
- }
-
- // This is no benchmark. It merely exist to have an easy way to pretty print the curently
- // configured `Schedule` during benchmark development.
- // It can be outputed using the following command:
- // cargo run --manifest-path=bin/node/cli/Cargo.toml --release \
- // --features runtime-benchmarks -- benchmark --dev --execution=native \
- // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares
- #[extra]
- print_schedule {
- #[cfg(feature = "std")]
- {
- let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1) -
- T::WeightInfo::on_initialize_per_trie_key(0);
- let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1) -
- T::WeightInfo::on_initialize_per_queue_item(0);
- let weight_limit = T::DeletionWeightLimit::get();
- let queue_depth: u64 = T::DeletionQueueDepth::get().into();
- println!("{:#?}", Schedule::<T>::default());
- println!("###############################################");
- println!("Lazy deletion throughput per block (empty queue, full queue): {}, {}",
- weight_limit / weight_per_key,
- (weight_limit - weight_per_queue_item * queue_depth) / weight_per_key,
- );
- }
- #[cfg(not(feature = "std"))]
- return Err("Run this bench with a native runtime in order to see the schedule.");
- }: {}
-}
-
-impl_benchmark_test_suite!(
- Contracts,
- crate::tests::ExtBuilder::default().build(),
- crate::tests::Test,
-);
pallets/contracts/src/benchmarking/sandbox.rsdiffbeforeafterboth--- a/pallets/contracts/src/benchmarking/sandbox.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-///! For instruction benchmarking we do no instantiate a full contract but merely the
-///! sandbox to execute the wasm code. This is because we do not need the full
-///! environment that provides the seal interface as imported functions.
-use super::{Config, code::WasmModule};
-use sp_core::crypto::UncheckedFrom;
-use sp_sandbox::{EnvironmentDefinitionBuilder, Instance, Memory};
-
-/// Minimal execution environment without any exported functions.
-pub struct Sandbox {
- instance: Instance<()>,
- _memory: Option<Memory>,
-}
-
-impl Sandbox {
- /// Invoke the `call` function of a contract code and panic on any execution error.
- pub fn invoke(&mut self) {
- self.instance.invoke("call", &[], &mut ()).unwrap();
- }
-}
-
-impl<T: Config> From<&WasmModule<T>> for Sandbox
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Creates an instance from the supplied module and supplies as much memory
- /// to the instance as the module declares as imported.
- fn from(module: &WasmModule<T>) -> Self {
- let mut env_builder = EnvironmentDefinitionBuilder::new();
- let memory = module.add_memory(&mut env_builder);
- let instance = Instance::new(&module.code, &env_builder, &mut ())
- .expect("Failed to create benchmarking Sandbox instance");
- Self {
- instance,
- _memory: memory,
- }
- }
-}
pallets/contracts/src/chain_extension.rsdiffbeforeafterboth--- a/pallets/contracts/src/chain_extension.rs
+++ /dev/null
@@ -1,400 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020 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.
-
-//! A mechanism for runtime authors to augment the functionality of contracts.
-//!
-//! The runtime is able to call into any contract and retrieve the result using
-//! [`bare_call`](crate::Pallet::bare_call). This already allows customization of runtime
-//! behaviour by user generated code (contracts). However, often it is more straightforward
-//! to allow the reverse behaviour: The contract calls into the runtime. We call the latter
-//! one a "chain extension" because it allows the chain to extend the set of functions that are
-//! callable by a contract.
-//!
-//! In order to create a chain extension the runtime author implements the [`ChainExtension`]
-//! trait and declares it in this pallet's [configuration Trait](crate::Config). All types
-//! required for this endeavour are defined or re-exported in this module. There is an
-//! implementation on `()` which can be used to signal that no chain extension is available.
-//!
-//! # Security
-//!
-//! The chain author alone is responsible for the security of the chain extension.
-//! This includes avoiding the exposure of exploitable functions and charging the
-//! appropriate amount of weight. In order to do so benchmarks must be written and the
-//! [`charge_weight`](Environment::charge_weight) function must be called **before**
-//! carrying out any action that causes the consumption of the chargeable weight.
-//! It cannot be overstated how delicate of a process the creation of a chain extension
-//! is. Check whether using [`bare_call`](crate::Pallet::bare_call) suffices for the
-//! use case at hand.
-//!
-//! # Benchmarking
-//!
-//! The builtin contract callable functions that pallet-contracts provides all have
-//! benchmarks that determine the correct weight that an invocation of these functions
-//! induces. In order to be able to charge the correct weight for the functions defined
-//! by a chain extension benchmarks must be written, too. In the near future this crate
-//! will provide the means for easier creation of those specialized benchmarks.
-//!
-//! # Example
-//!
-//! The ink! repository maintains an
-//! [end-to-end example](https://github.com/paritytech/ink/tree/master/examples/rand-extension)
-//! on how to use a chain extension in order to provide new features to ink! contracts.
-
-use crate::{
- Error,
- wasm::{Runtime, RuntimeToken},
-};
-use codec::Decode;
-use frame_support::weights::Weight;
-use sp_runtime::DispatchError;
-use sp_std::{marker::PhantomData, vec::Vec};
-
-pub use frame_system::Config as SysConfig;
-pub use pallet_contracts_primitives::ReturnFlags;
-pub use sp_core::crypto::UncheckedFrom;
-pub use crate::{Config, exec::Ext};
-pub use state::Init as InitState;
-
-/// Result that returns a [`DispatchError`] on error.
-pub type Result<T> = sp_std::result::Result<T, DispatchError>;
-
-/// A trait used to extend the set of contract callable functions.
-///
-/// In order to create a custom chain extension this trait must be implemented and supplied
-/// to the pallet contracts configuration trait as the associated type of the same name.
-/// Consult the [module documentation](self) for a general explanation of chain extensions.
-pub trait ChainExtension<C: Config> {
- /// Call the chain extension logic.
- ///
- /// This is the only function that needs to be implemented in order to write a
- /// chain extensions. It is called whenever a contract calls the `seal_call_chain_extension`
- /// imported wasm function.
- ///
- /// # Parameters
- /// - `func_id`: The first argument to `seal_call_chain_extension`. Usually used to
- /// determine which function to realize.
- /// - `env`: Access to the remaining arguments and the execution environment.
- ///
- /// # Return
- ///
- /// In case of `Err` the contract execution is immediately suspended and the passed error
- /// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit
- /// behaviour.
- fn call<E>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal>
- where
- E: Ext<T = C>,
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>;
-
- /// Determines whether chain extensions are enabled for this chain.
- ///
- /// The default implementation returns `true`. Therefore it is not necessary to overwrite
- /// this function when implementing a chain extension. In case of `false` the deployment of
- /// a contract that references `seal_call_chain_extension` will be denied and calling this
- /// function will return [`NoChainExtension`](Error::NoChainExtension) without first calling
- /// into [`call`](Self::call).
- fn enabled() -> bool {
- true
- }
-}
-
-/// Implementation that indicates that no chain extension is available.
-impl<C: Config> ChainExtension<C> for () {
- fn call<E>(_func_id: u32, mut _env: Environment<E, InitState>) -> Result<RetVal>
- where
- E: Ext<T = C>,
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
- {
- // Never called since [`Self::enabled()`] is set to `false`. Because we want to
- // avoid panics at all costs we supply a sensible error value here instead
- // of an `unimplemented!`.
- Err(Error::<E::T>::NoChainExtension.into())
- }
-
- fn enabled() -> bool {
- false
- }
-}
-
-/// Determines the exit behaviour and return value of a chain extension.
-pub enum RetVal {
- /// The chain extensions returns the supplied value to its calling contract.
- Converging(u32),
- /// The control does **not** return to the calling contract.
- ///
- /// Use this to stop the execution of the contract when the chain extension returns.
- /// The semantic is the same as for calling `seal_return`: The control returns to
- /// the caller of the currently executing contract yielding the supplied buffer and
- /// flags.
- Diverging { flags: ReturnFlags, data: Vec<u8> },
-}
-
-/// Grants the chain extension access to its parameters and execution environment.
-///
-/// It uses [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html)
-/// to enforce the correct usage of the parameters passed to the chain extension.
-pub struct Environment<'a, 'b, E: Ext, S: state::State> {
- /// The actual data of this type.
- inner: Inner<'a, 'b, E>,
- /// `S` is only used in the type system but never as value.
- phantom: PhantomData<S>,
-}
-
-/// Functions that are available in every state of this type.
-impl<'a, 'b, E: Ext, S: state::State> Environment<'a, 'b, E, S>
-where
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
-{
- /// Charge the passed `amount` of weight from the overall limit.
- ///
- /// It returns `Ok` when there the remaining weight budget is larger than the passed
- /// `weight`. It returns `Err` otherwise. In this case the chain extension should
- /// abort the execution and pass through the error.
- ///
- /// # Note
- ///
- /// Weight is synonymous with gas in substrate.
- pub fn charge_weight(&mut self, amount: Weight) -> Result<()> {
- self.inner
- .runtime
- .charge_gas(RuntimeToken::ChainExtension(amount))
- .map(|_| ())
- }
-
- /// Grants access to the execution environment of the current contract call.
- ///
- /// Consult the functions on the returned type before re-implementing those functions.
- pub fn ext(&mut self) -> &mut E {
- self.inner.runtime.ext()
- }
-}
-
-/// Functions that are only available in the initial state of this type.
-///
-/// Those are the functions that determine how the arguments to the chain extensions
-/// should be consumed.
-impl<'a, 'b, E: Ext> Environment<'a, 'b, E, state::Init> {
- /// Creates a new environment for consumption by a chain extension.
- ///
- /// It is only available to this crate because only the wasm runtime module needs to
- /// ever create this type. Chain extensions merely consume it.
- pub(crate) fn new(
- runtime: &'a mut Runtime<'b, E>,
- input_ptr: u32,
- input_len: u32,
- output_ptr: u32,
- output_len_ptr: u32,
- ) -> Self {
- Environment {
- inner: Inner {
- runtime,
- input_ptr,
- input_len,
- output_ptr,
- output_len_ptr,
- },
- phantom: PhantomData,
- }
- }
-
- /// Use all arguments as integer values.
- pub fn only_in(self) -> Environment<'a, 'b, E, state::OnlyIn> {
- Environment {
- inner: self.inner,
- phantom: PhantomData,
- }
- }
-
- /// Use input arguments as integer and output arguments as pointer to a buffer.
- pub fn prim_in_buf_out(self) -> Environment<'a, 'b, E, state::PrimInBufOut> {
- Environment {
- inner: self.inner,
- phantom: PhantomData,
- }
- }
-
- /// Use input and output arguments as pointers to a buffer.
- pub fn buf_in_buf_out(self) -> Environment<'a, 'b, E, state::BufInBufOut> {
- Environment {
- inner: self.inner,
- phantom: PhantomData,
- }
- }
-}
-
-/// Functions to use the input arguments as integers.
-impl<'a, 'b, E: Ext, S: state::PrimIn> Environment<'a, 'b, E, S> {
- /// The `input_ptr` argument.
- pub fn val0(&self) -> u32 {
- self.inner.input_ptr
- }
-
- /// The `input_len` argument.
- pub fn val1(&self) -> u32 {
- self.inner.input_len
- }
-}
-
-/// Functions to use the output arguments as integers.
-impl<'a, 'b, E: Ext, S: state::PrimOut> Environment<'a, 'b, E, S> {
- /// The `output_ptr` argument.
- pub fn val2(&self) -> u32 {
- self.inner.output_ptr
- }
-
- /// The `output_len_ptr` argument.
- pub fn val3(&self) -> u32 {
- self.inner.output_len_ptr
- }
-}
-
-/// Functions to use the input arguments as pointer to a buffer.
-impl<'a, 'b, E: Ext, S: state::BufIn> Environment<'a, 'b, E, S>
-where
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
-{
- /// Reads `min(max_len, in_len)` from contract memory.
- ///
- /// This does **not** charge any weight. The caller must make sure that the an
- /// appropriate amount of weight is charged **before** reading from contract memory.
- /// The reason for that is that usually the costs for reading data and processing
- /// said data cannot be separated in a benchmark. Therefore a chain extension would
- /// charge the overall costs either using `max_len` (worst case approximation) or using
- /// [`in_len()`](Self::in_len).
- pub fn read(&self, max_len: u32) -> Result<Vec<u8>> {
- self.inner
- .runtime
- .read_sandbox_memory(self.inner.input_ptr, self.inner.input_len.min(max_len))
- }
-
- /// Reads `min(buffer.len(), in_len) from contract memory.
- ///
- /// This takes a mutable pointer to a buffer fills it with data and shrinks it to
- /// the size of the actual data. Apart from supporting pre-allocated buffers it is
- /// equivalent to to [`read()`](Self::read).
- pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> {
- let len = buffer.len();
- let sliced = {
- let buffer = core::mem::take(buffer);
- &mut buffer[..len.min(self.inner.input_len as usize)]
- };
- self.inner
- .runtime
- .read_sandbox_memory_into_buf(self.inner.input_ptr, sliced)?;
- *buffer = sliced;
- Ok(())
- }
-
- /// Reads `in_len` from contract memory and scale decodes it.
- ///
- /// This function is secure and recommended for all input types of fixed size
- /// as long as the cost of reading the memory is included in the overall already charged
- /// weight of the chain extension. This should usually be the case when fixed input types
- /// are used. Non fixed size types (like everything using `Vec`) usually need to use
- /// [`in_len()`](Self::in_len) in order to properly charge the necessary weight.
- pub fn read_as<T: Decode>(&mut self) -> Result<T> {
- self.inner
- .runtime
- .read_sandbox_memory_as(self.inner.input_ptr, self.inner.input_len)
- }
-
- /// The length of the input as passed in as `input_len`.
- ///
- /// A chain extension would use this value to calculate the dynamic part of its
- /// weight. For example a chain extension that calculates the hash of some passed in
- /// bytes would use `in_len` to charge the costs of hashing that amount of bytes.
- /// This also subsumes the act of copying those bytes as a benchmarks measures both.
- pub fn in_len(&self) -> u32 {
- self.inner.input_len
- }
-}
-
-/// Functions to use the output arguments as pointer to a buffer.
-impl<'a, 'b, E: Ext, S: state::BufOut> Environment<'a, 'b, E, S>
-where
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
-{
- /// Write the supplied buffer to contract memory.
- ///
- /// If the contract supplied buffer is smaller than the passed `buffer` an `Err` is returned.
- /// If `allow_skip` is set to true the contract is allowed to skip the copying of the buffer
- /// by supplying the guard value of `u32::max_value()` as `out_ptr`. The
- /// `weight_per_byte` is only charged when the write actually happens and is not skipped or
- /// failed due to a too small output buffer.
- pub fn write(
- &mut self,
- buffer: &[u8],
- allow_skip: bool,
- weight_per_byte: Option<Weight>,
- ) -> Result<()> {
- self.inner.runtime.write_sandbox_output(
- self.inner.output_ptr,
- self.inner.output_len_ptr,
- buffer,
- allow_skip,
- |len| {
- weight_per_byte.map(|w| RuntimeToken::ChainExtension(w.saturating_mul(len.into())))
- },
- )
- }
-}
-
-/// The actual data of an `Environment`.
-///
-/// All data is put into this struct to easily pass it around as part of the typestate
-/// pattern. Also it creates the opportunity to box this struct in the future in case it
-/// gets too large.
-struct Inner<'a, 'b, E: Ext> {
- /// The runtime contains all necessary functions to interact with the running contract.
- runtime: &'a mut Runtime<'b, E>,
- /// Verbatim argument passed to `seal_call_chain_extension`.
- input_ptr: u32,
- /// Verbatim argument passed to `seal_call_chain_extension`.
- input_len: u32,
- /// Verbatim argument passed to `seal_call_chain_extension`.
- output_ptr: u32,
- /// Verbatim argument passed to `seal_call_chain_extension`.
- output_len_ptr: u32,
-}
-
-/// Private submodule with public types to prevent other modules from naming them.
-mod state {
- pub trait State {}
-
- pub trait PrimIn: State {}
- pub trait PrimOut: State {}
- pub trait BufIn: State {}
- pub trait BufOut: State {}
-
- /// The initial state of an [`Environment`](`super::Environment`).
- /// See [typestate programming](https://docs.rust-embedded.org/book/static-guarantees/typestate-programming.html).
- pub enum Init {}
- pub enum OnlyIn {}
- pub enum PrimInBufOut {}
- pub enum BufInBufOut {}
-
- impl State for Init {}
- impl State for OnlyIn {}
- impl State for PrimInBufOut {}
- impl State for BufInBufOut {}
-
- impl PrimIn for OnlyIn {}
- impl PrimOut for OnlyIn {}
- impl PrimIn for PrimInBufOut {}
- impl BufOut for PrimInBufOut {}
- impl BufIn for BufInBufOut {}
- impl BufOut for BufInBufOut {}
-}
pallets/contracts/src/exec.rsdiffbeforeafterboth--- a/pallets/contracts/src/exec.rs
+++ /dev/null
@@ -1,1836 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-use crate::{
- CodeHash, Event, Config, Pallet as Contracts, TrieId, BalanceOf, ContractInfo,
- gas::GasMeter,
- rent::Rent,
- storage::{self, Storage},
- Error, ContractInfoOf, Schedule, AliveContractInfo,
-};
-use sp_core::crypto::UncheckedFrom;
-use sp_std::{prelude::*, marker::PhantomData};
-use sp_runtime::{
- Perbill,
- traits::{Bounded, Zero, Convert, Saturating},
-};
-use frame_support::{
- dispatch::{DispatchResult, DispatchError},
- traits::{ExistenceRequirement, Currency, Time, Randomness, Get},
- weights::Weight,
- ensure,
-};
-use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
-
-pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
-pub type MomentOf<T> = <<T as Config>::Time as Time>::Moment;
-pub type SeedOf<T> = <T as frame_system::Config>::Hash;
-pub type BlockNumberOf<T> = <T as frame_system::Config>::BlockNumber;
-pub type StorageKey = [u8; 32];
-pub type ExecResult = Result<ExecReturnValue, ExecError>;
-
-/// A type that represents a topic of an event. At the moment a hash is used.
-pub type TopicOf<T> = <T as frame_system::Config>::Hash;
-
-/// Origin of the error.
-///
-/// Call or instantiate both called into other contracts and pass through errors happening
-/// in those to the caller. This enum is for the caller to distinguish whether the error
-/// happened during the execution of the callee or in the current execution context.
-#[cfg_attr(test, derive(Debug, PartialEq))]
-pub enum ErrorOrigin {
- /// Caller error origin.
- ///
- /// The error happened in the current exeuction context rather than in the one
- /// of the contract that is called into.
- Caller,
- /// The error happened during execution of the called contract.
- Callee,
-}
-
-/// Error returned by contract exection.
-#[cfg_attr(test, derive(Debug, PartialEq))]
-pub struct ExecError {
- /// The reason why the execution failed.
- pub error: DispatchError,
- /// Origin of the error.
- pub origin: ErrorOrigin,
-}
-
-impl<T: Into<DispatchError>> From<T> for ExecError {
- fn from(error: T) -> Self {
- Self {
- error: error.into(),
- origin: ErrorOrigin::Caller,
- }
- }
-}
-
-/// Information needed for rent calculations that can be requested by a contract.
-#[derive(codec::Encode)]
-#[cfg_attr(test, derive(Debug, PartialEq))]
-pub struct RentParams<T: Config> {
- /// The total balance of the contract. Includes the balance transferred from the caller.
- total_balance: BalanceOf<T>,
- /// The free balance of the contract. Includes the balance transferred from the caller.
- free_balance: BalanceOf<T>,
- /// See crate [`Contracts::subsistence_threshold()`].
- subsistence_threshold: BalanceOf<T>,
- /// See crate [`Config::DepositPerContract`].
- deposit_per_contract: BalanceOf<T>,
- /// See crate [`Config::DepositPerStorageByte`].
- deposit_per_storage_byte: BalanceOf<T>,
- /// See crate [`Config::DepositPerStorageItem`].
- deposit_per_storage_item: BalanceOf<T>,
- /// See crate [`Ext::rent_allowance()`].
- rent_allowance: BalanceOf<T>,
- /// See crate [`Config::RentFraction`].
- rent_fraction: Perbill,
- /// See crate [`AliveContractInfo::storage_size`].
- storage_size: u32,
- /// See crate [`Executable::aggregate_code_len()`].
- code_size: u32,
- /// See crate [`Executable::refcount()`].
- code_refcount: u32,
- /// Reserved for backwards compatible changes to this data structure.
- _reserved: Option<()>,
-}
-
-impl<T> RentParams<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- fn new<E: Executable<T>>(
- account_id: &T::AccountId,
- contract: &AliveContractInfo<T>,
- executable: &E,
- ) -> Self {
- Self {
- total_balance: T::Currency::total_balance(account_id),
- free_balance: T::Currency::free_balance(account_id),
- subsistence_threshold: <Contracts<T>>::subsistence_threshold(),
- deposit_per_contract: T::DepositPerContract::get(),
- deposit_per_storage_byte: T::DepositPerStorageByte::get(),
- deposit_per_storage_item: T::DepositPerStorageItem::get(),
- rent_allowance: contract.rent_allowance,
- rent_fraction: T::RentFraction::get(),
- storage_size: contract.storage_size,
- code_size: executable.aggregate_code_len(),
- code_refcount: executable.refcount(),
- _reserved: None,
- }
- }
-}
-
-/// We cannot derive `Default` because `T` does not necessarily implement `Default`.
-#[cfg(test)]
-impl<T: Config> Default for RentParams<T> {
- fn default() -> Self {
- Self {
- total_balance: Default::default(),
- free_balance: Default::default(),
- subsistence_threshold: Default::default(),
- deposit_per_contract: Default::default(),
- deposit_per_storage_byte: Default::default(),
- deposit_per_storage_item: Default::default(),
- rent_allowance: Default::default(),
- rent_fraction: Default::default(),
- storage_size: Default::default(),
- code_size: Default::default(),
- code_refcount: Default::default(),
- _reserved: Default::default(),
- }
- }
-}
-
-/// An interface that provides access to the external environment in which the
-/// smart-contract is executed.
-///
-/// This interface is specialized to an account of the executing code, so all
-/// operations are implicitly performed on that account.
-///
-/// # Note
-///
-/// This trait is sealed and cannot be implemented by downstream crates.
-pub trait Ext: sealing::Sealed {
- type T: Config;
-
- /// Returns the storage entry of the executing account by the given `key`.
- ///
- /// Returns `None` if the `key` wasn't previously set by `set_storage` or
- /// was deleted.
- fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>>;
-
- /// Sets the storage entry by the given key to the specified value. If `value` is `None` then
- /// the storage entry is deleted.
- fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult;
-
- /// Instantiate a contract from the given code.
- ///
- /// Returns the original code size of the called contract.
- /// The newly created account will be associated with `code`. `value` specifies the amount of value
- /// transferred from this to the newly created account (also known as endowment).
- ///
- /// # Return Value
- ///
- /// Result<(AccountId, ExecReturnValue, CodeSize), (ExecError, CodeSize)>
- fn instantiate(
- &mut self,
- code: CodeHash<Self::T>,
- value: BalanceOf<Self::T>,
- gas_meter: &mut GasMeter<Self::T>,
- input_data: Vec<u8>,
- salt: &[u8],
- ) -> Result<(AccountIdOf<Self::T>, ExecReturnValue, u32), (ExecError, u32)>;
-
- /// Transfer some amount of funds into the specified account.
- fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: BalanceOf<Self::T>) -> DispatchResult;
-
- /// Transfer all funds to `beneficiary` and delete the contract.
- ///
- /// Returns the original code size of the terminated contract.
- /// Since this function removes the self contract eagerly, if succeeded, no further actions should
- /// be performed on this `Ext` instance.
- ///
- /// This function will fail if the same contract is present on the contract
- /// call stack.
- ///
- /// # Return Value
- ///
- /// Result<CodeSize, (DispatchError, CodeSize)>
- fn terminate(
- &mut self,
- beneficiary: &AccountIdOf<Self::T>,
- ) -> Result<u32, (DispatchError, u32)>;
-
- /// Call (possibly transferring some amount of funds) into the specified account.
- ///
- /// Returns the original code size of the called contract.
- ///
- /// # Return Value
- ///
- /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
- fn call(
- &mut self,
- to: &AccountIdOf<Self::T>,
- value: BalanceOf<Self::T>,
- gas_meter: &mut GasMeter<Self::T>,
- input_data: Vec<u8>,
- ) -> Result<(ExecReturnValue, u32), (ExecError, u32)>;
-
- /// Restores the given destination contract sacrificing the current one.
- ///
- /// Since this function removes the self contract eagerly, if succeeded, no further actions should
- /// be performed on this `Ext` instance.
- ///
- /// This function will fail if the same contract is present
- /// on the contract call stack.
- ///
- /// # Return Value
- ///
- /// Result<(CallerCodeSize, DestCodeSize), (DispatchError, CallerCodeSize, DestCodesize)>
- fn restore_to(
- &mut self,
- dest: AccountIdOf<Self::T>,
- code_hash: CodeHash<Self::T>,
- rent_allowance: BalanceOf<Self::T>,
- delta: Vec<StorageKey>,
- ) -> Result<(u32, u32), (DispatchError, u32, u32)>;
-
- /// Returns a reference to the account id of the caller.
- fn caller(&self) -> &AccountIdOf<Self::T>;
-
- /// Returns a reference to the account id of the current contract.
- fn address(&self) -> &AccountIdOf<Self::T>;
-
- /// Returns the balance of the current contract.
- ///
- /// The `value_transferred` is already added.
- fn balance(&self) -> BalanceOf<Self::T>;
-
- /// Returns the value transferred along with this call or as endowment.
- fn value_transferred(&self) -> BalanceOf<Self::T>;
-
- /// Returns a reference to the timestamp of the current block
- fn now(&self) -> &MomentOf<Self::T>;
-
- /// Returns the minimum balance that is required for creating an account.
- fn minimum_balance(&self) -> BalanceOf<Self::T>;
-
- /// Returns the deposit required to create a tombstone upon contract eviction.
- fn tombstone_deposit(&self) -> BalanceOf<Self::T>;
-
- /// Returns a random number for the current block with the given subject.
- fn random(&self, subject: &[u8]) -> (SeedOf<Self::T>, BlockNumberOf<Self::T>);
-
- /// Deposit an event with the given topics.
- ///
- /// There should not be any duplicates in `topics`.
- fn deposit_event(&mut self, topics: Vec<TopicOf<Self::T>>, data: Vec<u8>);
-
- /// Set rent allowance of the contract
- fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<Self::T>);
-
- /// Rent allowance of the contract
- fn rent_allowance(&self) -> BalanceOf<Self::T>;
-
- /// Returns the current block number.
- fn block_number(&self) -> BlockNumberOf<Self::T>;
-
- /// Returns the maximum allowed size of a storage item.
- fn max_value_size(&self) -> u32;
-
- /// Returns the price for the specified amount of weight.
- fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T>;
-
- /// Get a reference to the schedule used by the current call.
- fn schedule(&self) -> &Schedule<Self::T>;
-
- /// Information needed for rent calculations.
- fn rent_params(&self) -> &RentParams<Self::T>;
-}
-
-/// Describes the different functions that can be exported by an [`Executable`].
-#[cfg_attr(test, derive(Clone, Copy, PartialEq))]
-pub enum ExportedFunction {
- /// The constructor function which is executed on deployment of a contract.
- Constructor,
- /// The function which is executed when a contract is called.
- Call,
-}
-
-/// A trait that represents something that can be executed.
-///
-/// In the on-chain environment this would be represented by a wasm module. This trait exists in
-/// order to be able to mock the wasm logic for testing.
-pub trait Executable<T: Config>: Sized {
- /// Load the executable from storage.
- fn from_storage(
- code_hash: CodeHash<T>,
- schedule: &Schedule<T>,
- gas_meter: &mut GasMeter<T>,
- ) -> Result<Self, DispatchError>;
-
- /// Load the module from storage without re-instrumenting it.
- ///
- /// A code module is re-instrumented on-load when it was originally instrumented with
- /// an older schedule. This skips this step for cases where the code storage is
- /// queried for purposes other than execution.
- fn from_storage_noinstr(code_hash: CodeHash<T>) -> Result<Self, DispatchError>;
-
- /// Decrements the refcount by one and deletes the code if it drops to zero.
- fn drop_from_storage(self);
-
- /// Increment the refcount by one. Fails if the code does not exist on-chain.
- ///
- /// Returns the size of the original code.
- fn add_user(code_hash: CodeHash<T>) -> Result<u32, DispatchError>;
-
- /// Decrement the refcount by one and remove the code when it drops to zero.
- ///
- /// Returns the size of the original code.
- fn remove_user(code_hash: CodeHash<T>) -> u32;
-
- /// Execute the specified exported function and return the result.
- ///
- /// When the specified function is `Constructor` the executable is stored and its
- /// refcount incremented.
- ///
- /// # Note
- ///
- /// This functions expects to be executed in a storage transaction that rolls back
- /// all of its emitted storage changes.
- fn execute<E: Ext<T = T>>(
- self,
- ext: E,
- function: &ExportedFunction,
- input_data: Vec<u8>,
- gas_meter: &mut GasMeter<T>,
- ) -> ExecResult;
-
- /// The code hash of the executable.
- fn code_hash(&self) -> &CodeHash<T>;
-
- /// The storage that is occupied by the instrumented executable and its pristine source.
- ///
- /// The returned size is already divided by the number of users who share the code.
- /// This is essentially `aggregate_code_len() / refcount()`.
- ///
- /// # Note
- ///
- /// This works with the current in-memory value of refcount. When calling any contract
- /// without refetching this from storage the result can be inaccurate as it might be
- /// working with a stale value. Usually this inaccuracy is tolerable.
- fn occupied_storage(&self) -> u32;
-
- /// Size of the instrumented code in bytes.
- fn code_len(&self) -> u32;
-
- /// Sum of instrumented and pristine code len.
- fn aggregate_code_len(&self) -> u32;
-
- // The number of contracts using this executable.
- fn refcount(&self) -> u32;
-}
-
-pub struct ExecutionContext<'a, T: Config + 'a, E> {
- caller: Option<&'a ExecutionContext<'a, T, E>>,
- self_account: T::AccountId,
- self_trie_id: Option<TrieId>,
- depth: usize,
- schedule: &'a Schedule<T>,
- timestamp: MomentOf<T>,
- block_number: T::BlockNumber,
- _phantom: PhantomData<E>,
-}
-
-impl<'a, T, E> ExecutionContext<'a, T, E>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
- E: Executable<T>,
-{
- /// Create the top level execution context.
- ///
- /// The specified `origin` address will be used as `sender` for. The `origin` must be a regular
- /// account (not a contract).
- pub fn top_level(origin: T::AccountId, schedule: &'a Schedule<T>) -> Self {
- ExecutionContext {
- caller: None,
- self_trie_id: None,
- self_account: origin,
- depth: 0,
- schedule,
- timestamp: T::Time::now(),
- block_number: <frame_system::Pallet<T>>::block_number(),
- _phantom: Default::default(),
- }
- }
-
- fn nested<'b, 'c: 'b>(
- &'c self,
- dest: T::AccountId,
- trie_id: TrieId,
- ) -> ExecutionContext<'b, T, E> {
- ExecutionContext {
- caller: Some(self),
- self_trie_id: Some(trie_id),
- self_account: dest,
- depth: self.depth + 1,
- schedule: self.schedule,
- timestamp: self.timestamp.clone(),
- block_number: self.block_number.clone(),
- _phantom: Default::default(),
- }
- }
-
- /// Make a call to the specified address, optionally transferring some funds.
- ///
- /// # Return Value
- ///
- /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
- pub fn call(
- &mut self,
- dest: T::AccountId,
- value: BalanceOf<T>,
- gas_meter: &mut GasMeter<T>,
- input_data: Vec<u8>,
- ) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
- if self.depth == T::MaxDepth::get() as usize {
- return Err((Error::<T>::MaxCallDepthReached.into(), 0));
- }
-
- let contract = <ContractInfoOf<T>>::get(&dest)
- .and_then(|contract| contract.get_alive())
- .ok_or((Error::<T>::NotCallable.into(), 0))?;
-
- let executable = E::from_storage(contract.code_hash, &self.schedule, gas_meter)
- .map_err(|e| (e.into(), 0))?;
- let code_len = executable.code_len();
-
- // This charges the rent and denies access to a contract that is in need of
- // eviction by returning `None`. We cannot evict eagerly here because those
- // changes would be rolled back in case this contract is called by another
- // contract.
- // See: https://github.com/paritytech/substrate/issues/6439#issuecomment-648754324
- let contract = Rent::<T, E>::charge(&dest, contract, executable.occupied_storage())
- .map_err(|e| (e.into(), code_len))?
- .ok_or((Error::<T>::NotCallable.into(), code_len))?;
-
- let transactor_kind = self.transactor_kind();
- let caller = self.self_account.clone();
-
- let result = self
- .with_nested_context(dest.clone(), contract.trie_id.clone(), |nested| {
- if value > BalanceOf::<T>::zero() {
- transfer::<T>(TransferCause::Call, transactor_kind, &caller, &dest, value)?
- }
-
- let call_context =
- nested.new_call_context(caller, &dest, value, &contract, &executable);
-
- let output = executable
- .execute(call_context, &ExportedFunction::Call, input_data, gas_meter)
- .map_err(|e| ExecError {
- error: e.error,
- origin: ErrorOrigin::Callee,
- })?;
- Ok(output)
- })
- .map_err(|e| (e, code_len))?;
- Ok((result, code_len))
- }
-
- pub fn instantiate(
- &mut self,
- endowment: BalanceOf<T>,
- gas_meter: &mut GasMeter<T>,
- executable: E,
- input_data: Vec<u8>,
- salt: &[u8],
- ) -> Result<(T::AccountId, ExecReturnValue), ExecError> {
- if self.depth == T::MaxDepth::get() as usize {
- Err(Error::<T>::MaxCallDepthReached)?
- }
-
- let transactor_kind = self.transactor_kind();
- let caller = self.self_account.clone();
- let dest = Contracts::<T>::contract_address(&caller, executable.code_hash(), salt);
-
- let output = frame_support::storage::with_transaction(|| {
- // Generate the trie id in a new transaction to only increment the counter on success.
- let dest_trie_id = Storage::<T>::generate_trie_id(&dest);
-
- let output = self.with_nested_context(dest.clone(), dest_trie_id, |nested| {
- let contract = Storage::<T>::place_contract(
- &dest,
- nested
- .self_trie_id
- .clone()
- .expect("the nested context always has to have self_trie_id"),
- executable.code_hash().clone(),
- )?;
-
- // Send funds unconditionally here. If the `endowment` is below existential_deposit
- // then error will be returned here.
- transfer::<T>(
- TransferCause::Instantiate,
- transactor_kind,
- &caller,
- &dest,
- endowment,
- )?;
-
- // Cache the value before calling into the constructor because that
- // consumes the value. If the constructor creates additional contracts using
- // the same code hash we still charge the "1 block rent" as if they weren't
- // spawned. This is OK as overcharging is always safe.
- let occupied_storage = executable.occupied_storage();
-
- let call_context = nested.new_call_context(
- caller.clone(),
- &dest,
- endowment,
- &contract,
- &executable,
- );
-
- let output = executable
- .execute(
- call_context,
- &ExportedFunction::Constructor,
- input_data,
- gas_meter,
- )
- .map_err(|e| ExecError {
- error: e.error,
- origin: ErrorOrigin::Callee,
- })?;
-
- // We need to re-fetch the contract because changes are written to storage
- // eagerly during execution.
- let contract = <ContractInfoOf<T>>::get(&dest)
- .and_then(|contract| contract.get_alive())
- .ok_or(Error::<T>::NotCallable)?;
-
- // Collect the rent for the first block to prevent the creation of very large
- // contracts that never intended to pay for even one block.
- // This also makes sure that it is above the subsistence threshold
- // in order to keep up the guarantuee that we always leave a tombstone behind
- // with the exception of a contract that called `seal_terminate`.
- Rent::<T, E>::charge(&dest, contract, occupied_storage)?
- .ok_or(Error::<T>::NewContractNotFunded)?;
-
- // Deposit an instantiation event.
- deposit_event::<T>(vec![], Event::Instantiated(caller.clone(), dest.clone()));
-
- Ok(output)
- });
-
- use frame_support::storage::TransactionOutcome::*;
- match output {
- Ok(_) => Commit(output),
- Err(_) => Rollback(output),
- }
- })?;
-
- Ok((dest, output))
- }
-
- fn new_call_context<'b>(
- &'b mut self,
- caller: T::AccountId,
- dest: &T::AccountId,
- value: BalanceOf<T>,
- contract: &AliveContractInfo<T>,
- executable: &E,
- ) -> CallContext<'b, 'a, T, E> {
- let timestamp = self.timestamp.clone();
- let block_number = self.block_number.clone();
- CallContext {
- ctx: self,
- caller,
- value_transferred: value,
- timestamp,
- block_number,
- rent_params: RentParams::new(dest, contract, executable),
- _phantom: Default::default(),
- }
- }
-
- /// Execute the given closure within a nested execution context.
- fn with_nested_context<F>(&mut self, dest: T::AccountId, trie_id: TrieId, func: F) -> ExecResult
- where
- F: FnOnce(&mut ExecutionContext<T, E>) -> ExecResult,
- {
- use frame_support::storage::TransactionOutcome::*;
- let mut nested = self.nested(dest, trie_id);
- frame_support::storage::with_transaction(|| {
- let output = func(&mut nested);
- match output {
- Ok(ref rv) if !rv.flags.contains(ReturnFlags::REVERT) => Commit(output),
- _ => Rollback(output),
- }
- })
- }
-
- /// Returns whether a contract, identified by address, is currently live in the execution
- /// stack, meaning it is in the middle of an execution.
- fn is_live(&self, account: &T::AccountId) -> bool {
- &self.self_account == account || self.caller.map_or(false, |caller| caller.is_live(account))
- }
-
- fn transactor_kind(&self) -> TransactorKind {
- if self.depth == 0 {
- debug_assert!(self.self_trie_id.is_none());
- debug_assert!(self.caller.is_none());
- debug_assert!(ContractInfoOf::<T>::get(&self.self_account).is_none());
- TransactorKind::PlainAccount
- } else {
- TransactorKind::Contract
- }
- }
-}
-
-/// Describes whether we deal with a contract or a plain account.
-enum TransactorKind {
- /// Transaction was initiated from a plain account. That can be either be through a
- /// signed transaction or through RPC.
- PlainAccount,
- /// The call was initiated by a contract account.
- Contract,
-}
-
-/// Describes possible transfer causes.
-enum TransferCause {
- Call,
- Instantiate,
- Terminate,
-}
-
-/// Transfer some funds from `transactor` to `dest`.
-///
-/// We only allow allow for draining all funds of the sender if `cause` is
-/// is specified as `Terminate`. Otherwise, any transfer that would bring the sender below the
-/// subsistence threshold (for contracts) or the existential deposit (for plain accounts)
-/// results in an error.
-fn transfer<T: Config>(
- cause: TransferCause,
- origin: TransactorKind,
- transactor: &T::AccountId,
- dest: &T::AccountId,
- value: BalanceOf<T>,
-) -> DispatchResult
-where
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- use self::TransferCause::*;
- use self::TransactorKind::*;
-
- // Only seal_terminate is allowed to bring the sender below the subsistence
- // threshold or even existential deposit.
- let existence_requirement = match (cause, origin) {
- (Terminate, _) => ExistenceRequirement::AllowDeath,
- (_, Contract) => {
- ensure!(
- T::Currency::total_balance(transactor).saturating_sub(value)
- >= Contracts::<T>::subsistence_threshold(),
- Error::<T>::BelowSubsistenceThreshold,
- );
- ExistenceRequirement::KeepAlive
- }
- (_, PlainAccount) => ExistenceRequirement::KeepAlive,
- };
-
- T::Currency::transfer(transactor, dest, value, existence_requirement)
- .map_err(|_| Error::<T>::TransferFailed)?;
-
- Ok(())
-}
-
-/// A context that is active within a call.
-///
-/// This context has some invariants that must be held at all times. Specifically:
-///`ctx` always points to a context of an alive contract. That implies that it has an existent
-/// `self_trie_id`.
-///
-/// Be advised that there are brief time spans where these invariants could be invalidated.
-/// For example, when a contract requests self-termination the contract is removed eagerly. That
-/// implies that the control won't be returned to the contract anymore, but there is still some code
-/// on the path of the return from that call context. Therefore, care must be taken in these
-/// situations.
-struct CallContext<'a, 'b: 'a, T: Config + 'b, E> {
- ctx: &'a mut ExecutionContext<'b, T, E>,
- caller: T::AccountId,
- value_transferred: BalanceOf<T>,
- timestamp: MomentOf<T>,
- block_number: T::BlockNumber,
- rent_params: RentParams<T>,
- _phantom: PhantomData<E>,
-}
-
-impl<'a, 'b: 'a, T, E> Ext for CallContext<'a, 'b, T, E>
-where
- T: Config + 'b,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
- E: Executable<T>,
-{
- type T = T;
-
- fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
- let trie_id = self.ctx.self_trie_id.as_ref().expect(
- "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\
- it cannot be `None`;\
- expect can't fail;\
- qed",
- );
- Storage::<T>::read(trie_id, key)
- }
-
- fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> DispatchResult {
- let trie_id = self.ctx.self_trie_id.as_ref().expect(
- "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\
- it cannot be `None`;\
- expect can't fail;\
- qed",
- );
- // write panics if the passed account is not alive.
- // the contract must be in the alive state within the `CallContext`;\
- // the contract cannot be absent in storage;
- // write cannot return `None`;
- // qed
- Storage::<T>::write(&self.ctx.self_account, trie_id, &key, value)
- }
-
- fn instantiate(
- &mut self,
- code_hash: CodeHash<T>,
- endowment: BalanceOf<T>,
- gas_meter: &mut GasMeter<T>,
- input_data: Vec<u8>,
- salt: &[u8],
- ) -> Result<(AccountIdOf<T>, ExecReturnValue, u32), (ExecError, u32)> {
- let executable =
- E::from_storage(code_hash, &self.ctx.schedule, gas_meter).map_err(|e| (e.into(), 0))?;
- let code_len = executable.code_len();
- self.ctx
- .instantiate(endowment, gas_meter, executable, input_data, salt)
- .map(|r| (r.0, r.1, code_len))
- .map_err(|e| (e, code_len))
- }
-
- fn transfer(&mut self, to: &T::AccountId, value: BalanceOf<T>) -> DispatchResult {
- transfer::<T>(
- TransferCause::Call,
- TransactorKind::Contract,
- &self.ctx.self_account.clone(),
- to,
- value,
- )
- }
-
- fn terminate(
- &mut self,
- beneficiary: &AccountIdOf<Self::T>,
- ) -> Result<u32, (DispatchError, u32)> {
- let self_id = self.ctx.self_account.clone();
- let value = T::Currency::free_balance(&self_id);
- if let Some(caller_ctx) = self.ctx.caller {
- if caller_ctx.is_live(&self_id) {
- return Err((Error::<T>::ReentranceDenied.into(), 0));
- }
- }
- transfer::<T>(
- TransferCause::Terminate,
- TransactorKind::Contract,
- &self_id,
- beneficiary,
- value,
- )
- .map_err(|e| (e, 0))?;
- if let Some(ContractInfo::Alive(info)) = ContractInfoOf::<T>::take(&self_id) {
- Storage::<T>::queue_trie_for_deletion(&info).map_err(|e| (e, 0))?;
- let code_len = E::remove_user(info.code_hash);
- Contracts::<T>::deposit_event(Event::Terminated(self_id, beneficiary.clone()));
- Ok(code_len)
- } else {
- panic!(
- "this function is only invoked by in the context of a contract;\
- this contract is therefore alive;\
- qed"
- );
- }
- }
-
- fn call(
- &mut self,
- to: &T::AccountId,
- value: BalanceOf<T>,
- gas_meter: &mut GasMeter<T>,
- input_data: Vec<u8>,
- ) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
- self.ctx.call(to.clone(), value, gas_meter, input_data)
- }
-
- fn restore_to(
- &mut self,
- dest: AccountIdOf<Self::T>,
- code_hash: CodeHash<Self::T>,
- rent_allowance: BalanceOf<Self::T>,
- delta: Vec<StorageKey>,
- ) -> Result<(u32, u32), (DispatchError, u32, u32)> {
- if let Some(caller_ctx) = self.ctx.caller {
- if caller_ctx.is_live(&self.ctx.self_account) {
- return Err((Error::<T>::ReentranceDenied.into(), 0, 0));
- }
- }
-
- let result = Rent::<T, E>::restore_to(
- self.ctx.self_account.clone(),
- dest.clone(),
- code_hash.clone(),
- rent_allowance,
- delta,
- );
- if let Ok(_) = result {
- deposit_event::<Self::T>(
- vec![],
- Event::Restored(
- self.ctx.self_account.clone(),
- dest,
- code_hash,
- rent_allowance,
- ),
- );
- }
- result
- }
-
- fn address(&self) -> &T::AccountId {
- &self.ctx.self_account
- }
-
- fn caller(&self) -> &T::AccountId {
- &self.caller
- }
-
- fn balance(&self) -> BalanceOf<T> {
- T::Currency::free_balance(&self.ctx.self_account)
- }
-
- fn value_transferred(&self) -> BalanceOf<T> {
- self.value_transferred
- }
-
- fn random(&self, subject: &[u8]) -> (SeedOf<T>, BlockNumberOf<T>) {
- T::Randomness::random(subject)
- }
-
- fn now(&self) -> &MomentOf<T> {
- &self.timestamp
- }
-
- fn minimum_balance(&self) -> BalanceOf<T> {
- T::Currency::minimum_balance()
- }
-
- fn tombstone_deposit(&self) -> BalanceOf<T> {
- T::TombstoneDeposit::get()
- }
-
- fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
- deposit_event::<Self::T>(
- topics,
- Event::ContractEmitted(self.ctx.self_account.clone(), data),
- );
- }
-
- fn set_rent_allowance(&mut self, rent_allowance: BalanceOf<T>) {
- if let Err(storage::ContractAbsentError) =
- Storage::<T>::set_rent_allowance(&self.ctx.self_account, rent_allowance)
- {
- panic!(
- "`self_account` points to an alive contract within the `CallContext`;
- set_rent_allowance cannot return `Err`; qed"
- );
- }
- }
-
- fn rent_allowance(&self) -> BalanceOf<T> {
- Storage::<T>::rent_allowance(&self.ctx.self_account)
- .unwrap_or_else(|_| <BalanceOf<T>>::max_value()) // Must never be triggered actually
- }
-
- fn block_number(&self) -> T::BlockNumber {
- self.block_number
- }
-
- fn max_value_size(&self) -> u32 {
- T::MaxValueSize::get()
- }
-
- fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
- T::WeightPrice::convert(weight)
- }
-
- fn schedule(&self) -> &Schedule<Self::T> {
- &self.ctx.schedule
- }
-
- fn rent_params(&self) -> &RentParams<Self::T> {
- &self.rent_params
- }
-}
-
-fn deposit_event<T: Config>(topics: Vec<T::Hash>, event: Event<T>) {
- <frame_system::Pallet<T>>::deposit_event_indexed(
- &*topics,
- <T as Config>::Event::from(event).into(),
- )
-}
-
-mod sealing {
- use super::*;
-
- pub trait Sealed {}
-
- impl<'a, 'b: 'a, T: Config, E> Sealed for CallContext<'a, 'b, T, E> {}
-
- #[cfg(test)]
- impl Sealed for crate::wasm::MockExt {}
-
- #[cfg(test)]
- impl Sealed for &mut crate::wasm::MockExt {}
-}
-
-/// These tests exercise the executive layer.
-///
-/// In these tests the VM/loader are mocked. Instead of dealing with wasm bytecode they use simple closures.
-/// This allows you to tackle executive logic more thoroughly without writing a
-/// wasm VM code.
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::{
- gas::GasMeter,
- tests::{ExtBuilder, Test, Event as MetaEvent},
- storage::{Storage, ContractAbsentError},
- tests::{
- ALICE, BOB, CHARLIE,
- test_utils::{place_contract, set_balance, get_balance},
- },
- exec::ExportedFunction::*,
- Error, Weight, CurrentSchedule,
- };
- use sp_core::Bytes;
- use frame_support::assert_noop;
- use sp_runtime::DispatchError;
- use assert_matches::assert_matches;
- use std::{cell::RefCell, collections::HashMap, rc::Rc};
- use pretty_assertions::{assert_eq, assert_ne};
-
- type MockContext<'a> = ExecutionContext<'a, Test, MockExecutable>;
-
- const GAS_LIMIT: Weight = 10_000_000_000;
-
- thread_local! {
- static LOADER: RefCell<MockLoader> = RefCell::new(MockLoader::default());
- }
-
- fn events() -> Vec<Event<Test>> {
- <frame_system::Pallet<Test>>::events()
- .into_iter()
- .filter_map(|meta| match meta.event {
- MetaEvent::pallet_contracts(contract_event) => Some(contract_event),
- _ => None,
- })
- .collect()
- }
-
- struct MockCtx<'a> {
- ext: &'a mut dyn Ext<T = Test>,
- input_data: Vec<u8>,
- gas_meter: &'a mut GasMeter<Test>,
- }
-
- #[derive(Clone)]
- struct MockExecutable {
- func: Rc<dyn Fn(MockCtx, &Self) -> ExecResult + 'static>,
- func_type: ExportedFunction,
- code_hash: CodeHash<Test>,
- refcount: u64,
- }
-
- #[derive(Default)]
- struct MockLoader {
- map: HashMap<CodeHash<Test>, MockExecutable>,
- counter: u64,
- }
-
- impl MockLoader {
- fn insert(
- func_type: ExportedFunction,
- f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static,
- ) -> CodeHash<Test> {
- LOADER.with(|loader| {
- let mut loader = loader.borrow_mut();
- // Generate code hashes as monotonically increasing values.
- let hash = <Test as frame_system::Config>::Hash::from_low_u64_be(loader.counter);
- loader.counter += 1;
- loader.map.insert(
- hash,
- MockExecutable {
- func: Rc::new(f),
- func_type,
- code_hash: hash.clone(),
- refcount: 1,
- },
- );
- hash
- })
- }
-
- fn increment_refcount(code_hash: CodeHash<Test>) {
- LOADER.with(|loader| {
- let mut loader = loader.borrow_mut();
- loader
- .map
- .entry(code_hash)
- .and_modify(|executable| executable.refcount += 1)
- .or_insert_with(|| panic!("code_hash does not exist"));
- });
- }
-
- fn decrement_refcount(code_hash: CodeHash<Test>) {
- use std::collections::hash_map::Entry::Occupied;
- LOADER.with(|loader| {
- let mut loader = loader.borrow_mut();
- let mut entry = match loader.map.entry(code_hash) {
- Occupied(e) => e,
- _ => panic!("code_hash does not exist"),
- };
- let refcount = &mut entry.get_mut().refcount;
- *refcount -= 1;
- if *refcount == 0 {
- entry.remove();
- }
- });
- }
-
- fn refcount(code_hash: &CodeHash<Test>) -> u32 {
- LOADER.with(|loader| {
- loader
- .borrow()
- .map
- .get(code_hash)
- .expect("code_hash does not exist")
- .refcount()
- })
- }
- }
-
- impl Executable<Test> for MockExecutable {
- fn from_storage(
- code_hash: CodeHash<Test>,
- _schedule: &Schedule<Test>,
- _gas_meter: &mut GasMeter<Test>,
- ) -> Result<Self, DispatchError> {
- Self::from_storage_noinstr(code_hash)
- }
-
- fn from_storage_noinstr(code_hash: CodeHash<Test>) -> Result<Self, DispatchError> {
- LOADER.with(|loader| {
- loader
- .borrow_mut()
- .map
- .get(&code_hash)
- .cloned()
- .ok_or(Error::<Test>::CodeNotFound.into())
- })
- }
-
- fn drop_from_storage(self) {
- MockLoader::decrement_refcount(self.code_hash);
- }
-
- fn add_user(code_hash: CodeHash<Test>) -> Result<u32, DispatchError> {
- MockLoader::increment_refcount(code_hash);
- Ok(0)
- }
-
- fn remove_user(code_hash: CodeHash<Test>) -> u32 {
- MockLoader::decrement_refcount(code_hash);
- 0
- }
-
- fn execute<E: Ext<T = Test>>(
- self,
- mut ext: E,
- function: &ExportedFunction,
- input_data: Vec<u8>,
- gas_meter: &mut GasMeter<Test>,
- ) -> ExecResult {
- if let &Constructor = function {
- MockLoader::increment_refcount(self.code_hash);
- }
- if function == &self.func_type {
- (self.func)(
- MockCtx {
- ext: &mut ext,
- input_data,
- gas_meter,
- },
- &self,
- )
- } else {
- exec_success()
- }
- }
-
- fn code_hash(&self) -> &CodeHash<Test> {
- &self.code_hash
- }
-
- fn occupied_storage(&self) -> u32 {
- 0
- }
-
- fn code_len(&self) -> u32 {
- 0
- }
-
- fn aggregate_code_len(&self) -> u32 {
- 0
- }
-
- fn refcount(&self) -> u32 {
- self.refcount as u32
- }
- }
-
- fn exec_success() -> ExecResult {
- Ok(ExecReturnValue {
- flags: ReturnFlags::empty(),
- data: Bytes(Vec::new()),
- })
- }
-
- #[test]
- fn it_works() {
- thread_local! {
- static TEST_DATA: RefCell<Vec<usize>> = RefCell::new(vec![0]);
- }
-
- let value = Default::default();
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let exec_ch = MockLoader::insert(Call, |_ctx, _executable| {
- TEST_DATA.with(|data| data.borrow_mut().push(1));
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- place_contract(&BOB, exec_ch);
-
- assert_matches!(ctx.call(BOB, value, &mut gas_meter, vec![]), Ok(_));
- });
-
- TEST_DATA.with(|data| assert_eq!(*data.borrow(), vec![0, 1]));
- }
-
- #[test]
- fn transfer_works() {
- // This test verifies that a contract is able to transfer
- // some funds to another account.
- let origin = ALICE;
- let dest = BOB;
-
- ExtBuilder::default().build().execute_with(|| {
- set_balance(&origin, 100);
- set_balance(&dest, 0);
-
- super::transfer::<Test>(
- super::TransferCause::Call,
- super::TransactorKind::PlainAccount,
- &origin,
- &dest,
- 55,
- )
- .unwrap();
-
- assert_eq!(get_balance(&origin), 45);
- assert_eq!(get_balance(&dest), 55);
- });
- }
-
- #[test]
- fn changes_are_reverted_on_failing_call() {
- // This test verifies that changes are reverted on a call which fails (or equally, returns
- // a non-zero status code).
- let origin = ALICE;
- let dest = BOB;
-
- let return_ch = MockLoader::insert(Call, |_, _| {
- Ok(ExecReturnValue {
- flags: ReturnFlags::REVERT,
- data: Bytes(Vec::new()),
- })
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(origin.clone(), &schedule);
- place_contract(&BOB, return_ch);
- set_balance(&origin, 100);
- let balance = get_balance(&dest);
-
- let output = ctx
- .call(
- dest.clone(),
- 55,
- &mut GasMeter::<Test>::new(GAS_LIMIT),
- vec![],
- )
- .unwrap();
-
- assert!(!output.0.is_success());
- assert_eq!(get_balance(&origin), 100);
-
- // the rent is still charged
- assert!(get_balance(&dest) < balance);
- });
- }
-
- #[test]
- fn balance_too_low() {
- // This test verifies that a contract can't send value if it's
- // balance is too low.
- let origin = ALICE;
- let dest = BOB;
-
- ExtBuilder::default().build().execute_with(|| {
- set_balance(&origin, 0);
-
- let result = super::transfer::<Test>(
- super::TransferCause::Call,
- super::TransactorKind::PlainAccount,
- &origin,
- &dest,
- 100,
- );
-
- assert_eq!(result, Err(Error::<Test>::TransferFailed.into()));
- assert_eq!(get_balance(&origin), 0);
- assert_eq!(get_balance(&dest), 0);
- });
- }
-
- #[test]
- fn output_is_returned_on_success() {
- // Verifies that if a contract returns data with a successful exit status, this data
- // is returned from the execution context.
- let origin = ALICE;
- let dest = BOB;
- let return_ch = MockLoader::insert(Call, |_, _| {
- Ok(ExecReturnValue {
- flags: ReturnFlags::empty(),
- data: Bytes(vec![1, 2, 3, 4]),
- })
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(origin, &schedule);
- place_contract(&BOB, return_ch);
-
- let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
-
- let output = result.unwrap();
- assert!(output.0.is_success());
- assert_eq!(output.0.data, Bytes(vec![1, 2, 3, 4]));
- });
- }
-
- #[test]
- fn output_is_returned_on_failure() {
- // Verifies that if a contract returns data with a failing exit status, this data
- // is returned from the execution context.
- let origin = ALICE;
- let dest = BOB;
- let return_ch = MockLoader::insert(Call, |_, _| {
- Ok(ExecReturnValue {
- flags: ReturnFlags::REVERT,
- data: Bytes(vec![1, 2, 3, 4]),
- })
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(origin, &schedule);
- place_contract(&BOB, return_ch);
-
- let result = ctx.call(dest, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
-
- let output = result.unwrap();
- assert!(!output.0.is_success());
- assert_eq!(output.0.data, Bytes(vec![1, 2, 3, 4]));
- });
- }
-
- #[test]
- fn input_data_to_call() {
- let input_data_ch = MockLoader::insert(Call, |ctx, _| {
- assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
- exec_success()
- });
-
- // This one tests passing the input data into a contract via call.
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- place_contract(&BOB, input_data_ch);
-
- let result = ctx.call(
- BOB,
- 0,
- &mut GasMeter::<Test>::new(GAS_LIMIT),
- vec![1, 2, 3, 4],
- );
- assert_matches!(result, Ok(_));
- });
- }
-
- #[test]
- fn input_data_to_instantiate() {
- let input_data_ch = MockLoader::insert(Constructor, |ctx, _| {
- assert_eq!(ctx.input_data, &[1, 2, 3, 4]);
- exec_success()
- });
-
- // This one tests passing the input data into a contract via instantiate.
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(input_data_ch, &schedule, &mut gas_meter).unwrap();
-
- set_balance(&ALICE, subsistence * 10);
-
- let result = ctx.instantiate(
- subsistence * 3,
- &mut gas_meter,
- executable,
- vec![1, 2, 3, 4],
- &[],
- );
- assert_matches!(result, Ok(_));
- });
- }
-
- #[test]
- fn max_depth() {
- // This test verifies that when we reach the maximal depth creation of an
- // yet another context fails.
- thread_local! {
- static REACHED_BOTTOM: RefCell<bool> = RefCell::new(false);
- }
- let value = Default::default();
- let recurse_ch = MockLoader::insert(Call, |ctx, _| {
- // Try to call into yourself.
- let r = ctx.ext.call(&BOB, 0, ctx.gas_meter, vec![]);
-
- REACHED_BOTTOM.with(|reached_bottom| {
- let mut reached_bottom = reached_bottom.borrow_mut();
- if !*reached_bottom {
- // We are first time here, it means we just reached bottom.
- // Verify that we've got proper error and set `reached_bottom`.
- assert_eq!(r, Err((Error::<Test>::MaxCallDepthReached.into(), 0)));
- *reached_bottom = true;
- } else {
- // We just unwinding stack here.
- assert_matches!(r, Ok(_));
- }
- });
-
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- set_balance(&BOB, 1);
- place_contract(&BOB, recurse_ch);
-
- let result = ctx.call(BOB, value, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
-
- assert_matches!(result, Ok(_));
- });
- }
-
- #[test]
- fn caller_returns_proper_values() {
- let origin = ALICE;
- let dest = BOB;
-
- thread_local! {
- static WITNESSED_CALLER_BOB: RefCell<Option<AccountIdOf<Test>>> = RefCell::new(None);
- static WITNESSED_CALLER_CHARLIE: RefCell<Option<AccountIdOf<Test>>> = RefCell::new(None);
- }
-
- let bob_ch = MockLoader::insert(Call, |ctx, _| {
- // Record the caller for bob.
- WITNESSED_CALLER_BOB
- .with(|caller| *caller.borrow_mut() = Some(ctx.ext.caller().clone()));
-
- // Call into CHARLIE contract.
- assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));
- exec_success()
- });
- let charlie_ch = MockLoader::insert(Call, |ctx, _| {
- // Record the caller for charlie.
- WITNESSED_CALLER_CHARLIE
- .with(|caller| *caller.borrow_mut() = Some(ctx.ext.caller().clone()));
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(origin.clone(), &schedule);
- place_contract(&dest, bob_ch);
- place_contract(&CHARLIE, charlie_ch);
-
- let result = ctx.call(
- dest.clone(),
- 0,
- &mut GasMeter::<Test>::new(GAS_LIMIT),
- vec![],
- );
-
- assert_matches!(result, Ok(_));
- });
-
- WITNESSED_CALLER_BOB.with(|caller| assert_eq!(*caller.borrow(), Some(origin)));
- WITNESSED_CALLER_CHARLIE.with(|caller| assert_eq!(*caller.borrow(), Some(dest)));
- }
-
- #[test]
- fn address_returns_proper_values() {
- let bob_ch = MockLoader::insert(Call, |ctx, _| {
- // Verify that address matches BOB.
- assert_eq!(*ctx.ext.address(), BOB);
-
- // Call into charlie contract.
- assert_matches!(ctx.ext.call(&CHARLIE, 0, ctx.gas_meter, vec![]), Ok(_));
- exec_success()
- });
- let charlie_ch = MockLoader::insert(Call, |ctx, _| {
- assert_eq!(*ctx.ext.address(), CHARLIE);
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- place_contract(&BOB, bob_ch);
- place_contract(&CHARLIE, charlie_ch);
-
- let result = ctx.call(BOB, 0, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]);
-
- assert_matches!(result, Ok(_));
- });
- }
-
- #[test]
- fn refuse_instantiate_with_value_below_existential_deposit() {
- let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
-
- assert_matches!(
- ctx.instantiate(
- 0, // <- zero endowment
- &mut gas_meter,
- executable,
- vec![],
- &[],
- ),
- Err(_)
- );
- });
- }
-
- #[test]
- fn instantiation_work_with_success_output() {
- let dummy_ch = MockLoader::insert(Constructor, |_, _| {
- Ok(ExecReturnValue {
- flags: ReturnFlags::empty(),
- data: Bytes(vec![80, 65, 83, 83]),
- })
- });
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
- set_balance(&ALICE, 1000);
-
- let instantiated_contract_address = assert_matches!(
- ctx.instantiate(
- 100,
- &mut gas_meter,
- executable,
- vec![],
- &[],
- ),
- Ok((address, ref output)) if output.data == Bytes(vec![80, 65, 83, 83]) => address
- );
-
- // Check that the newly created account has the expected code hash and
- // there are instantiation event.
- assert_eq!(
- Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),
- dummy_ch
- );
- assert_eq!(
- &events(),
- &[Event::Instantiated(ALICE, instantiated_contract_address)]
- );
- });
- }
-
- #[test]
- fn instantiation_fails_with_failing_output() {
- let dummy_ch = MockLoader::insert(Constructor, |_, _| {
- Ok(ExecReturnValue {
- flags: ReturnFlags::REVERT,
- data: Bytes(vec![70, 65, 73, 76]),
- })
- });
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(dummy_ch, &schedule, &mut gas_meter).unwrap();
- set_balance(&ALICE, 1000);
-
- let instantiated_contract_address = assert_matches!(
- ctx.instantiate(
- 100,
- &mut gas_meter,
- executable,
- vec![],
- &[],
- ),
- Ok((address, ref output)) if output.data == Bytes(vec![70, 65, 73, 76]) => address
- );
-
- // Check that the account has not been created.
- assert_noop!(
- Storage::<Test>::code_hash(&instantiated_contract_address),
- ContractAbsentError,
- );
- assert!(events().is_empty());
- });
- }
-
- #[test]
- fn instantiation_from_contract() {
- let dummy_ch = MockLoader::insert(Call, |_, _| exec_success());
- let instantiated_contract_address = Rc::new(RefCell::new(None::<AccountIdOf<Test>>));
- let instantiator_ch = MockLoader::insert(Call, {
- let dummy_ch = dummy_ch.clone();
- let instantiated_contract_address = Rc::clone(&instantiated_contract_address);
- move |ctx, _| {
- // Instantiate a contract and save it's address in `instantiated_contract_address`.
- let (address, output, _) = ctx
- .ext
- .instantiate(
- dummy_ch,
- Contracts::<Test>::subsistence_threshold() * 3,
- ctx.gas_meter,
- vec![],
- &[48, 49, 50],
- )
- .unwrap();
-
- *instantiated_contract_address.borrow_mut() = address.into();
- Ok(output)
- }
- });
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);
- place_contract(&BOB, instantiator_ch);
-
- assert_matches!(
- ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
- Ok(_)
- );
-
- let instantiated_contract_address = instantiated_contract_address
- .borrow()
- .as_ref()
- .unwrap()
- .clone();
-
- // Check that the newly created account has the expected code hash and
- // there are instantiation event.
- assert_eq!(
- Storage::<Test>::code_hash(&instantiated_contract_address).unwrap(),
- dummy_ch
- );
- assert_eq!(
- &events(),
- &[Event::Instantiated(BOB, instantiated_contract_address)]
- );
- });
- }
-
- #[test]
- fn instantiation_traps() {
- let dummy_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into()));
- let instantiator_ch = MockLoader::insert(Call, {
- let dummy_ch = dummy_ch.clone();
- move |ctx, _| {
- // Instantiate a contract and save it's address in `instantiated_contract_address`.
- assert_matches!(
- ctx.ext
- .instantiate(dummy_ch, 15u64, ctx.gas_meter, vec![], &[],),
- Err((
- ExecError {
- error: DispatchError::Other("It's a trap!"),
- origin: ErrorOrigin::Callee,
- },
- 0
- ))
- );
-
- exec_success()
- }
- });
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- set_balance(&ALICE, 1000);
- set_balance(&BOB, 100);
- place_contract(&BOB, instantiator_ch);
-
- assert_matches!(
- ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
- Ok(_)
- );
-
- // The contract wasn't instantiated so we don't expect to see an instantiation
- // event here.
- assert_eq!(&events(), &[]);
- });
- }
-
- #[test]
- fn termination_from_instantiate_fails() {
- let terminate_ch = MockLoader::insert(Constructor, |ctx, _| {
- ctx.ext.terminate(&ALICE).unwrap();
- exec_success()
- });
-
- ExtBuilder::default()
- .existential_deposit(15)
- .build()
- .execute_with(|| {
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(terminate_ch, &schedule, &mut gas_meter).unwrap();
- set_balance(&ALICE, 1000);
-
- assert_eq!(
- ctx.instantiate(100, &mut gas_meter, executable, vec![], &[],),
- Err(Error::<Test>::NotCallable.into())
- );
-
- assert_eq!(&events(), &[]);
- });
- }
-
- #[test]
- fn rent_allowance() {
- let rent_allowance_ch = MockLoader::insert(Constructor, |ctx, _| {
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let allowance = subsistence * 3;
- assert_eq!(ctx.ext.rent_allowance(), <BalanceOf<Test>>::max_value());
- ctx.ext.set_rent_allowance(allowance);
- assert_eq!(ctx.ext.rent_allowance(), allowance);
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- let executable =
- MockExecutable::from_storage(rent_allowance_ch, &schedule, &mut gas_meter).unwrap();
- set_balance(&ALICE, subsistence * 10);
-
- let result = ctx.instantiate(subsistence * 5, &mut gas_meter, executable, vec![], &[]);
- assert_matches!(result, Ok(_));
- });
- }
-
- #[test]
- fn rent_params_works() {
- let code_hash = MockLoader::insert(Call, |ctx, executable| {
- let address = ctx.ext.address();
- let contract = <ContractInfoOf<Test>>::get(address)
- .and_then(|c| c.get_alive())
- .unwrap();
- assert_eq!(
- ctx.ext.rent_params(),
- &RentParams::new(address, &contract, executable)
- );
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- set_balance(&ALICE, subsistence * 10);
- place_contract(&BOB, code_hash);
- ctx.call(BOB, 0, &mut gas_meter, vec![]).unwrap();
- });
- }
-
- #[test]
- fn rent_params_snapshotted() {
- let code_hash = MockLoader::insert(Call, |ctx, executable| {
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let address = ctx.ext.address();
- let contract = <ContractInfoOf<Test>>::get(address)
- .and_then(|c| c.get_alive())
- .unwrap();
- let rent_params = RentParams::new(address, &contract, executable);
-
- // Changing the allowance during the call: rent params stay unchanged.
- let allowance = 42;
- assert_ne!(allowance, rent_params.rent_allowance);
- ctx.ext.set_rent_allowance(allowance);
- assert_eq!(ctx.ext.rent_params(), &rent_params);
-
- // Creating another instance from the same code_hash increases the refcount.
- // This is also not reflected in the rent params.
- assert_eq!(MockLoader::refcount(&executable.code_hash), 1);
- ctx.ext
- .instantiate(
- executable.code_hash,
- subsistence * 25,
- &mut GasMeter::<Test>::new(GAS_LIMIT),
- vec![],
- &[],
- )
- .unwrap();
- assert_eq!(MockLoader::refcount(&executable.code_hash), 2);
- assert_eq!(ctx.ext.rent_params(), &rent_params);
-
- exec_success()
- });
-
- ExtBuilder::default().build().execute_with(|| {
- let subsistence = Contracts::<Test>::subsistence_threshold();
- let schedule = <CurrentSchedule<Test>>::get();
- let mut ctx = MockContext::top_level(ALICE, &schedule);
- let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
- set_balance(&ALICE, subsistence * 100);
- place_contract(&BOB, code_hash);
- ctx.call(BOB, subsistence * 50, &mut gas_meter, vec![])
- .unwrap();
- });
- }
-}
pallets/contracts/src/gas.rsdiffbeforeafterboth--- a/pallets/contracts/src/gas.rs
+++ /dev/null
@@ -1,364 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-use crate::{Config, Error, exec::ExecError};
-use sp_std::marker::PhantomData;
-use sp_runtime::traits::Zero;
-use frame_support::{
- dispatch::{
- DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo, DispatchError,
- },
- weights::Weight,
-};
-use sp_core::crypto::UncheckedFrom;
-
-#[cfg(test)]
-use std::{any::Any, fmt::Debug};
-
-#[derive(Debug, PartialEq, Eq)]
-pub struct ChargedAmount(Weight);
-
-impl ChargedAmount {
- pub fn amount(&self) -> Weight {
- self.0
- }
-}
-
-#[cfg(not(test))]
-pub trait TestAuxiliaries {}
-#[cfg(not(test))]
-impl<T> TestAuxiliaries for T {}
-
-#[cfg(test)]
-pub trait TestAuxiliaries: Any + Debug + PartialEq + Eq {}
-#[cfg(test)]
-impl<T: Any + Debug + PartialEq + Eq> TestAuxiliaries for T {}
-
-/// This trait represents a token that can be used for charging `GasMeter`.
-/// There is no other way of charging it.
-///
-/// Implementing type is expected to be super lightweight hence `Copy` (`Clone` is added
-/// for consistency). If inlined there should be no observable difference compared
-/// to a hand-written code.
-pub trait Token<T: Config>: Copy + Clone + TestAuxiliaries {
- /// Metadata type, which the token can require for calculating the amount
- /// of gas to charge. Can be a some configuration type or
- /// just the `()`.
- type Metadata;
-
- /// Calculate amount of gas that should be taken by this token.
- ///
- /// This function should be really lightweight and must not fail. It is not
- /// expected that implementors will query the storage or do any kinds of heavy operations.
- ///
- /// That said, implementors of this function still can run into overflows
- /// while calculating the amount. In this case it is ok to use saturating operations
- /// since on overflow they will return `max_value` which should consume all gas.
- fn calculate_amount(&self, metadata: &Self::Metadata) -> Weight;
-}
-
-/// A wrapper around a type-erased trait object of what used to be a `Token`.
-#[cfg(test)]
-pub struct ErasedToken {
- pub description: String,
- pub token: Box<dyn Any>,
-}
-
-pub struct GasMeter<T: Config> {
- gas_limit: Weight,
- /// Amount of gas left from initial gas limit. Can reach zero.
- gas_left: Weight,
- _phantom: PhantomData<T>,
- #[cfg(test)]
- tokens: Vec<ErasedToken>,
-}
-
-impl<T: Config> GasMeter<T>
-where
- T::AccountId: UncheckedFrom<<T as frame_system::Config>::Hash> + AsRef<[u8]>,
-{
- pub fn new(gas_limit: Weight) -> Self {
- GasMeter {
- gas_limit,
- gas_left: gas_limit,
- _phantom: PhantomData,
- #[cfg(test)]
- tokens: Vec::new(),
- }
- }
-
- /// Account for used gas.
- ///
- /// Amount is calculated by the given `token`.
- ///
- /// Returns `OutOfGas` if there is not enough gas or addition of the specified
- /// amount of gas has lead to overflow. On success returns `Proceed`.
- ///
- /// NOTE that amount is always consumed, i.e. if there is not enough gas
- /// then the counter will be set to zero.
- #[inline]
- pub fn charge<Tok: Token<T>>(
- &mut self,
- metadata: &Tok::Metadata,
- token: Tok,
- ) -> Result<ChargedAmount, DispatchError> {
- #[cfg(test)]
- {
- // Unconditionally add the token to the storage.
- let erased_tok = ErasedToken {
- description: format!("{:?}", token),
- token: Box::new(token),
- };
- self.tokens.push(erased_tok);
- }
-
- let amount = token.calculate_amount(metadata);
- let new_value = self.gas_left.checked_sub(amount);
-
- // We always consume the gas even if there is not enough gas.
- self.gas_left = new_value.unwrap_or_else(Zero::zero);
-
- match new_value {
- Some(_) => Ok(ChargedAmount(amount)),
- None => Err(Error::<T>::OutOfGas.into()),
- }
- }
-
- /// Adjust a previously charged amount down to its actual amount.
- ///
- /// This is when a maximum a priori amount was charged and then should be partially
- /// refunded to match the actual amount.
- pub fn adjust_gas<Tok: Token<T>>(
- &mut self,
- charged_amount: ChargedAmount,
- metadata: &Tok::Metadata,
- token: Tok,
- ) {
- let adjustment = charged_amount
- .0
- .saturating_sub(token.calculate_amount(metadata));
- self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit);
- }
-
- /// Refund previously charged gas back to the gas meter.
- ///
- /// This can be used if a gas worst case estimation must be charged before
- /// performing a certain action. This way the difference can be refundend when
- /// the worst case did not happen.
- pub fn refund(&mut self, amount: ChargedAmount) {
- self.gas_left = self.gas_left.saturating_add(amount.0).min(self.gas_limit)
- }
-
- /// Allocate some amount of gas and perform some work with
- /// a newly created nested gas meter.
- ///
- /// Invokes `f` with either the gas meter that has `amount` gas left or
- /// with `None`, if this gas meter has not enough gas to allocate given `amount`.
- ///
- /// All unused gas in the nested gas meter is returned to this gas meter.
- pub fn with_nested<R, F: FnOnce(Option<&mut GasMeter<T>>) -> R>(
- &mut self,
- amount: Weight,
- f: F,
- ) -> R {
- // NOTE that it is ok to allocate all available gas since it still ensured
- // by `charge` that it doesn't reach zero.
- if self.gas_left < amount {
- f(None)
- } else {
- self.gas_left = self.gas_left - amount;
- let mut nested = GasMeter::new(amount);
-
- let r = f(Some(&mut nested));
-
- self.gas_left = self.gas_left + nested.gas_left;
-
- r
- }
- }
-
- /// Returns how much gas was used.
- pub fn gas_spent(&self) -> Weight {
- self.gas_limit - self.gas_left
- }
-
- /// Returns how much gas left from the initial budget.
- pub fn gas_left(&self) -> Weight {
- self.gas_left
- }
-
- /// Turn this GasMeter into a DispatchResult that contains the actually used gas.
- pub fn into_dispatch_result<R, E>(
- self,
- result: Result<R, E>,
- base_weight: Weight,
- ) -> DispatchResultWithPostInfo
- where
- E: Into<ExecError>,
- {
- let post_info = PostDispatchInfo {
- actual_weight: Some(self.gas_spent().saturating_add(base_weight)),
- pays_fee: Default::default(),
- };
-
- result
- .map(|_| post_info)
- .map_err(|e| DispatchErrorWithPostInfo {
- post_info,
- error: e.into().error,
- })
- }
-
- #[cfg(test)]
- pub fn tokens(&self) -> &[ErasedToken] {
- &self.tokens
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{GasMeter, Token};
- use crate::tests::Test;
-
- /// A simple utility macro that helps to match against a
- /// list of tokens.
- macro_rules! match_tokens {
- ($tokens_iter:ident,) => {
- };
- ($tokens_iter:ident, $x:expr, $($rest:tt)*) => {
- {
- let next = ($tokens_iter).next().unwrap();
- let pattern = $x;
-
- // Note that we don't specify the type name directly in this macro,
- // we only have some expression $x of some type. At the same time, we
- // have an iterator of Box<dyn Any> and to downcast we need to specify
- // the type which we want downcast to.
- //
- // So what we do is we assign `_pattern_typed_next_ref` to a variable which has
- // the required type.
- //
- // Then we make `_pattern_typed_next_ref = token.downcast_ref()`. This makes
- // rustc infer the type `T` (in `downcast_ref<T: Any>`) to be the same as in $x.
-
- let mut _pattern_typed_next_ref = &pattern;
- _pattern_typed_next_ref = match next.token.downcast_ref() {
- Some(p) => {
- assert_eq!(p, &pattern);
- p
- }
- None => {
- panic!("expected type {} got {}", stringify!($x), next.description);
- }
- };
- }
-
- match_tokens!($tokens_iter, $($rest)*);
- };
- }
-
- /// A trivial token that charges the specified number of gas units.
- #[derive(Copy, Clone, PartialEq, Eq, Debug)]
- struct SimpleToken(u64);
- impl Token<Test> for SimpleToken {
- type Metadata = ();
- fn calculate_amount(&self, _metadata: &()) -> u64 {
- self.0
- }
- }
-
- struct MultiplierTokenMetadata {
- multiplier: u64,
- }
- /// A simple token that charges for the given amount multiplied to
- /// a multiplier taken from a given metadata.
- #[derive(Copy, Clone, PartialEq, Eq, Debug)]
- struct MultiplierToken(u64);
-
- impl Token<Test> for MultiplierToken {
- type Metadata = MultiplierTokenMetadata;
- fn calculate_amount(&self, metadata: &MultiplierTokenMetadata) -> u64 {
- // Probably you want to use saturating mul in production code.
- self.0 * metadata.multiplier
- }
- }
-
- #[test]
- fn it_works() {
- let gas_meter = GasMeter::<Test>::new(50000);
- assert_eq!(gas_meter.gas_left(), 50000);
- }
-
- #[test]
- fn simple() {
- let mut gas_meter = GasMeter::<Test>::new(50000);
-
- let result = gas_meter.charge(
- &MultiplierTokenMetadata { multiplier: 3 },
- MultiplierToken(10),
- );
- assert!(!result.is_err());
-
- assert_eq!(gas_meter.gas_left(), 49_970);
- }
-
- #[test]
- fn tracing() {
- let mut gas_meter = GasMeter::<Test>::new(50000);
- assert!(!gas_meter.charge(&(), SimpleToken(1)).is_err());
- assert!(!gas_meter
- .charge(
- &MultiplierTokenMetadata { multiplier: 3 },
- MultiplierToken(10)
- )
- .is_err());
-
- let mut tokens = gas_meter.tokens()[0..2].iter();
- match_tokens!(tokens, SimpleToken(1), MultiplierToken(10),);
- }
-
- // This test makes sure that nothing can be executed if there is no gas.
- #[test]
- fn refuse_to_execute_anything_if_zero() {
- let mut gas_meter = GasMeter::<Test>::new(0);
- assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
- }
-
- // Make sure that if the gas meter is charged by exceeding amount then not only an error
- // returned for that charge, but also for all consequent charges.
- //
- // This is not strictly necessary, because the execution should be interrupted immediately
- // if the gas meter runs out of gas. However, this is just a nice property to have.
- #[test]
- fn overcharge_is_unrecoverable() {
- let mut gas_meter = GasMeter::<Test>::new(200);
-
- // The first charge is should lead to OOG.
- assert!(gas_meter.charge(&(), SimpleToken(300)).is_err());
-
- // The gas meter is emptied at this moment, so this should also fail.
- assert!(gas_meter.charge(&(), SimpleToken(1)).is_err());
- }
-
- // Charging the exact amount that the user paid for should be
- // possible.
- #[test]
- fn charge_exact_amount() {
- let mut gas_meter = GasMeter::<Test>::new(25);
- assert!(!gas_meter.charge(&(), SimpleToken(25)).is_err());
- }
-}
pallets/contracts/src/lib.rsdiffbeforeafterboth--- a/pallets/contracts/src/lib.rs
+++ /dev/null
@@ -1,831 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-//! # Contract Pallet
-//!
-//! The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts.
-//!
-//! - [`Config`]
-//! - [`Call`]
-//!
-//! ## Overview
-//!
-//! This module extends accounts based on the [`Currency`] trait to have smart-contract functionality. It can
-//! be used with other modules that implement accounts based on [`Currency`]. 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 in a code cache, 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.
-//!
-//! ### Gas
-//!
-//! Senders must specify a gas limit with every call, as all instructions invoked by the smart-contract require gas.
-//! Unused gas is refunded after the call, regardless of the execution outcome.
-//!
-//! If the gas 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::update_schedule`] -
-//! ([Root Origin](https://substrate.dev/docs/en/knowledgebase/runtime/origin) Only) -
-//! Set a new [`Schedule`].
-//! * [`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::claim_surcharge`] - Evict a contract that cannot pay rent anymore.
-//!
-//! ## Usage
-//!
-//! The Contract module is a work in progress. The following examples show how this Contract module
-//! can be used to instantiate and call contracts.
-//!
-//! * [`ink`](https://github.com/paritytech/ink) is
-//! an [`eDSL`](https://wiki.haskell.org/Embedded_domain_specific_language) that enables writing
-//! WebAssembly based smart contracts in the Rust programming language. This is a work in progress.
-//!
-//! ## Related Modules
-//!
-//! * [Balances](../pallet_balances/index.html)
-
-#![cfg_attr(not(feature = "std"), no_std)]
-#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "512")]
-
-#[macro_use]
-mod gas;
-mod benchmarking;
-mod exec;
-mod migration;
-mod rent;
-mod schedule;
-mod storage;
-mod wasm;
-
-pub mod chain_extension;
-pub mod weights;
-
-#[cfg(test)]
-mod tests;
-
-pub use crate::{pallet::*, schedule::Schedule};
-use crate::{
- gas::GasMeter,
- exec::{ExecutionContext, Executable},
- rent::Rent,
- storage::{Storage, DeletedContract, ContractInfo, AliveContractInfo, TombstoneContractInfo},
- weights::WeightInfo,
- wasm::PrefabWasmModule,
-};
-use sp_core::{Bytes, crypto::UncheckedFrom};
-use sp_std::prelude::*;
-use sp_runtime::{
- traits::{Hash, StaticLookup, Convert, Saturating, Zero},
- Perbill,
-};
-use frame_support::{
- traits::{OnUnbalanced, Currency, Get, Time, Randomness},
- weights::{Weight, PostDispatchInfo, WithPostDispatchInfo},
-};
-use frame_system::Pallet as System;
-use pallet_contracts_primitives::{
- RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult,
- ContractInstantiateResult, Code, InstantiateReturnValue,
-};
-
-type CodeHash<T> = <T as frame_system::Config>::Hash;
-type TrieId = Vec<u8>;
-type BalanceOf<T> =
- <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
-type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
- <T as frame_system::Config>::AccountId,
->>::NegativeImbalance;
-
-#[frame_support::pallet]
-pub mod pallet {
- use frame_support::pallet_prelude::*;
- use frame_system::pallet_prelude::*;
- use super::*;
-
- #[pallet::config]
- pub trait Config: frame_system::Config {
- /// The time implementation used to supply timestamps to conntracts through `seal_now`.
- type Time: Time;
-
- /// The generator used to supply randomness to contracts through `seal_random`.
- type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
-
- /// The currency in which fees are paid and contract balances are held.
- type Currency: Currency<Self::AccountId>;
-
- /// The overarching event type.
- type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
-
- /// Handler for rent payments.
- type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
-
- /// Number of block delay an extrinsic claim surcharge has.
- ///
- /// When claim surcharge is called by an extrinsic the rent is checked
- /// for current_block - delay
- #[pallet::constant]
- type SignedClaimHandicap: Get<Self::BlockNumber>;
-
- /// The minimum amount required to generate a tombstone.
- #[pallet::constant]
- type TombstoneDeposit: Get<BalanceOf<Self>>;
-
- /// The balance every contract needs to deposit to stay alive indefinitely.
- ///
- /// This is different from the [`Self::TombstoneDeposit`] because this only needs to be
- /// deposited while the contract is alive. Costs for additional storage are added to
- /// this base cost.
- ///
- /// This is a simple way to ensure that contracts with empty storage eventually get deleted by
- /// making them pay rent. This creates an incentive to remove them early in order to save rent.
- #[pallet::constant]
- type DepositPerContract: Get<BalanceOf<Self>>;
-
- /// The balance a contract needs to deposit per storage byte to stay alive indefinitely.
- ///
- /// Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day,
- /// then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent.
- /// But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000,
- /// then it would pay 500 BU/day.
- #[pallet::constant]
- type DepositPerStorageByte: Get<BalanceOf<Self>>;
-
- /// The balance a contract needs to deposit per storage item to stay alive indefinitely.
- ///
- /// It works the same as [`Self::DepositPerStorageByte`] but for storage items.
- #[pallet::constant]
- type DepositPerStorageItem: Get<BalanceOf<Self>>;
-
- /// The fraction of the deposit that should be used as rent per block.
- ///
- /// When a contract hasn't enough balance deposited to stay alive indefinitely it needs
- /// to pay per block for the storage it consumes that is not covered by the deposit.
- /// This determines how high this rent payment is per block as a fraction of the deposit.
- #[pallet::constant]
- type RentFraction: Get<Perbill>;
-
- /// Reward that is received by the party whose touch has led
- /// to removal of a contract.
- #[pallet::constant]
- type SurchargeReward: Get<BalanceOf<Self>>;
-
- /// The maximum nesting level of a call/instantiate stack.
- #[pallet::constant]
- type MaxDepth: Get<u32>;
-
- /// The maximum size of a storage value and event payload in bytes.
- #[pallet::constant]
- type MaxValueSize: Get<u32>;
-
- /// 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>;
-
- /// The maximum number of tries that can be queued for deletion.
- #[pallet::constant]
- type DeletionQueueDepth: Get<u32>;
-
- /// The maximum amount of weight that can be consumed per block for lazy trie removal.
- #[pallet::constant]
- type DeletionWeightLimit: Get<Weight>;
-
- /// The maximum length of a contract code in bytes. This limit applies to the instrumented
- /// version of the code. Therefore `instantiate_with_code` can fail even when supplying
- /// a wasm binary below this maximum size.
- #[pallet::constant]
- type MaxCodeSize: Get<u32>;
- }
-
- #[pallet::pallet]
- pub struct Pallet<T>(PhantomData<T>);
-
- #[pallet::hooks]
- impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
- where
- T::AccountId: UncheckedFrom<T::Hash>,
- T::AccountId: AsRef<[u8]>,
- {
- fn on_initialize(_block: T::BlockNumber) -> Weight {
- // We do not want to go above the block limit and rather avoid lazy deletion
- // in that case. This should only happen on runtime upgrades.
- let weight_limit = T::BlockWeights::get()
- .max_block
- .saturating_sub(System::<T>::block_weight().total())
- .min(T::DeletionWeightLimit::get());
- Storage::<T>::process_deletion_queue_batch(weight_limit)
- .saturating_add(T::WeightInfo::on_initialize())
- }
-
- fn on_runtime_upgrade() -> Weight {
- migration::migrate::<T>()
- }
- }
-
- #[pallet::call]
- impl<T: Config> Pallet<T>
- where
- T::AccountId: UncheckedFrom<T::Hash>,
- T::AccountId: AsRef<[u8]>,
- {
- /// Updates the schedule for metering contracts.
- ///
- /// The schedule's version cannot be less than the version of the stored schedule.
- /// If a schedule does not change the instruction weights the version does not
- /// need to be increased. Therefore we allow storing a schedule that has the same
- /// version as the stored one.
- #[pallet::weight(T::WeightInfo::update_schedule())]
- pub fn update_schedule(
- origin: OriginFor<T>,
- schedule: Schedule<T>,
- ) -> DispatchResultWithPostInfo {
- ensure_root(origin)?;
- if <CurrentSchedule<T>>::get().version > schedule.version {
- Err(Error::<T>::InvalidScheduleVersion)?
- }
- Self::deposit_event(Event::ScheduleUpdated(schedule.version));
- CurrentSchedule::put(schedule);
- Ok(().into())
- }
-
- /// Makes a call to an account, optionally transferring some balance.
- ///
- /// * 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::weight(T::WeightInfo::call(T::MaxCodeSize::get() / 1024).saturating_add(*gas_limit))]
- pub fn call(
- origin: OriginFor<T>,
- dest: <T::Lookup as StaticLookup>::Source,
- #[pallet::compact] value: BalanceOf<T>,
- #[pallet::compact] gas_limit: Weight,
- data: Vec<u8>,
- ) -> DispatchResultWithPostInfo {
- let origin = ensure_signed(origin)?;
- let dest = T::Lookup::lookup(dest)?;
- let mut gas_meter = GasMeter::new(gas_limit);
- let schedule = <CurrentSchedule<T>>::get();
- let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
- let (result, code_len) = match ctx.call(dest, value, &mut gas_meter, data) {
- Ok((output, len)) => (Ok(output), len),
- Err((err, len)) => (Err(err), len),
- };
- gas_meter.into_dispatch_result(result, T::WeightInfo::call(code_len / 1024))
- }
-
- /// Instantiates a new contract from the supplied `code` optionally transferring
- /// some balance.
- ///
- /// This is the only function that can deploy new code to the chain.
- ///
- /// # Parameters
- ///
- /// * `endowment`: The balance to transfer from the `origin` to the newly created contract.
- /// * `gas_limit`: The gas limit enforced when executing the constructor.
- /// * `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 instrumented, 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 `endowment` is transferred to the new account.
- /// - The `deploy` function is executed in the context of the newly-created account.
- #[pallet::weight(
- T::WeightInfo::instantiate_with_code(
- code.len() as u32 / 1024,
- salt.len() as u32 / 1024,
- )
- .saturating_add(*gas_limit)
- )]
- pub fn instantiate_with_code(
- origin: OriginFor<T>,
- #[pallet::compact] endowment: BalanceOf<T>,
- #[pallet::compact] gas_limit: Weight,
- code: Vec<u8>,
- data: Vec<u8>,
- salt: Vec<u8>,
- ) -> DispatchResultWithPostInfo {
- let origin = ensure_signed(origin)?;
- let code_len = code.len() as u32;
- ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
- let mut gas_meter = GasMeter::new(gas_limit);
- let schedule = <CurrentSchedule<T>>::get();
- let executable = PrefabWasmModule::from_code(code, &schedule)?;
- let code_len = executable.code_len();
- ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
- let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
- let result = ctx
- .instantiate(endowment, &mut gas_meter, executable, data, &salt)
- .map(|(_address, output)| output);
- gas_meter.into_dispatch_result(
- result,
- T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024),
- )
- }
-
- /// 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::weight(
- T::WeightInfo::instantiate(T::MaxCodeSize::get() / 1024, salt.len() as u32 / 1024)
- .saturating_add(*gas_limit)
- )]
- pub fn instantiate(
- origin: OriginFor<T>,
- #[pallet::compact] endowment: BalanceOf<T>,
- #[pallet::compact] gas_limit: Weight,
- code_hash: CodeHash<T>,
- data: Vec<u8>,
- salt: Vec<u8>,
- ) -> DispatchResultWithPostInfo {
- let origin = ensure_signed(origin)?;
- let mut gas_meter = GasMeter::new(gas_limit);
- let schedule = <CurrentSchedule<T>>::get();
- let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
- let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
- let code_len = executable.code_len();
- let result = ctx
- .instantiate(endowment, &mut gas_meter, executable, data, &salt)
- .map(|(_address, output)| output);
- gas_meter.into_dispatch_result(
- result,
- T::WeightInfo::instantiate(code_len / 1024, salt.len() as u32 / 1024),
- )
- }
-
- /// Allows block producers to claim a small reward for evicting a contract. If a block
- /// producer fails to do so, a regular users will be allowed to claim the reward.
- ///
- /// In case of a successful eviction no fees are charged from the sender. However, the
- /// reward is capped by the total amount of rent that was payed by the contract while
- /// it was alive.
- ///
- /// If contract is not evicted as a result of this call, [`Error::ContractNotEvictable`]
- /// is returned and the sender is not eligible for the reward.
- #[pallet::weight(T::WeightInfo::claim_surcharge(T::MaxCodeSize::get() / 1024))]
- pub fn claim_surcharge(
- origin: OriginFor<T>,
- dest: T::AccountId,
- aux_sender: Option<T::AccountId>,
- ) -> DispatchResultWithPostInfo {
- let origin = origin.into();
- let (signed, rewarded) = match (origin, aux_sender) {
- (Ok(frame_system::RawOrigin::Signed(account)), None) => (true, account),
- (Ok(frame_system::RawOrigin::None), Some(aux_sender)) => (false, aux_sender),
- _ => Err(Error::<T>::InvalidSurchargeClaim)?,
- };
-
- // Add some advantage for block producers (who send unsigned extrinsics) by
- // adding a handicap: for signed extrinsics we use a slightly older block number
- // for the eviction check. This can be viewed as if we pushed regular users back in past.
- let handicap = if signed {
- T::SignedClaimHandicap::get()
- } else {
- Zero::zero()
- };
-
- // If poking the contract has lead to eviction of the contract, give out the rewards.
- match Rent::<T, PrefabWasmModule<T>>::try_eviction(&dest, handicap)? {
- (Some(rent_payed), code_len) => T::Currency::deposit_into_existing(
- &rewarded,
- T::SurchargeReward::get().min(rent_payed),
- )
- .map(|_| PostDispatchInfo {
- actual_weight: Some(T::WeightInfo::claim_surcharge(code_len / 1024)),
- pays_fee: Pays::No,
- })
- .map_err(Into::into),
- (None, code_len) => Err(Error::<T>::ContractNotEvictable
- .with_weight(T::WeightInfo::claim_surcharge(code_len / 1024))),
- }
- }
- }
-
- #[pallet::event]
- #[pallet::generate_deposit(pub(super) fn deposit_event)]
- #[pallet::metadata(T::AccountId = "AccountId", T::Hash = "Hash", BalanceOf<T> = "Balance")]
- pub enum Event<T: Config> {
- /// Contract deployed by address at the specified address. \[deployer, contract\]
- Instantiated(T::AccountId, T::AccountId),
-
- /// Contract has been evicted and is now in tombstone state. \[contract\]
- Evicted(T::AccountId),
-
- /// Contract has been terminated without leaving a tombstone.
- /// \[contract, beneficiary\]
- ///
- /// # Params
- ///
- /// - `contract`: The contract that was terminated.
- /// - `beneficiary`: The account that received the contracts remaining balance.
- ///
- /// # Note
- ///
- /// The only way for a contract to be removed without a tombstone and emitting
- /// this event is by calling `seal_terminate`.
- Terminated(T::AccountId, T::AccountId),
-
- /// Restoration of a contract has been successful.
- /// \[restorer, dest, code_hash, rent_allowance\]
- ///
- /// # Params
- ///
- /// - `restorer`: Account ID of the restoring contract.
- /// - `dest`: Account ID of the restored contract.
- /// - `code_hash`: Code hash of the restored contract.
- /// - `rent_allowance`: Rent allowance of the restored contract.
- Restored(T::AccountId, T::AccountId, T::Hash, BalanceOf<T>),
-
- /// Code with the specified hash has been stored. \[code_hash\]
- CodeStored(T::Hash),
-
- /// Triggered when the current schedule is updated.
- /// \[version\]
- ///
- /// # Params
- ///
- /// - `version`: The version of the newly set schedule.
- ScheduleUpdated(u32),
-
- /// A custom event emitted by the contract.
- /// \[contract, data\]
- ///
- /// # Params
- ///
- /// - `contract`: The contract that emitted the event.
- /// - `data`: Data supplied by the contract. Metadata generated during contract
- /// compilation is needed to decode it.
- ContractEmitted(T::AccountId, Vec<u8>),
-
- /// A code with the specified hash was removed.
- /// \[code_hash\]
- ///
- /// This happens when the last contract that uses this code hash was removed or evicted.
- CodeRemoved(T::Hash),
- }
-
- #[pallet::error]
- pub enum Error<T> {
- /// A new schedule must have a greater version than the current one.
- InvalidScheduleVersion,
- /// An origin must be signed or inherent and auxiliary sender only provided on inherent.
- InvalidSurchargeClaim,
- /// Cannot restore from nonexisting or tombstone contract.
- InvalidSourceContract,
- /// Cannot restore to nonexisting or alive contract.
- InvalidDestinationContract,
- /// Tombstones don't match.
- InvalidTombstone,
- /// An origin TrieId written in the current block.
- InvalidContractOrigin,
- /// 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 would have brought the contract below
- /// the subsistence threshold. No transfer is allowed to do this in order to allow
- /// for a tombstone to be created. Use `seal_terminate` to remove a contract without
- /// leaving a tombstone behind.
- BelowSubsistenceThreshold,
- /// The newly created contract is below the subsistence threshold after executing
- /// its contructor. No contracts are allowed to exist below that threshold.
- NewContractNotFunded,
- /// Performing the requested transfer failed for a reason originating in the
- /// chosen currency implementation of the runtime. Most probably the balance is
- /// too low or locks are placed on it.
- TransferFailed,
- /// Performing a call was denied because the calling depth reached the limit
- /// of what is specified in the schedule.
- MaxCallDepthReached,
- /// The contract that was called is either no contract at all (a plain account)
- /// or is a tombstone.
- NotCallable,
- /// 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,
- /// 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,
- /// The action performed is not allowed while the contract performing it is already
- /// on the call stack. Those actions are contract self destruction and restoration
- /// of a tombstone.
- ReentranceDenied,
- /// `seal_input` was called twice from the same contract execution context.
- InputAlreadyRead,
- /// The subject passed to `seal_random` exceeds the limit.
- RandomSubjectTooLong,
- /// The amount of topics passed to `seal_deposit_events` exceeds the limit.
- TooManyTopics,
- /// The topics passed to `seal_deposit_events` contains at least one duplicate.
- DuplicateTopics,
- /// 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,
- /// Removal of a contract failed because the deletion queue is full.
- ///
- /// This can happen when either calling [`Pallet::claim_surcharge`] or `seal_terminate`.
- /// The queue is filled by deleting contracts and emptied by a fixed amount each block.
- /// Trying again during another block is the only way to resolve this issue.
- DeletionQueueFull,
- /// A contract could not be evicted because it has enough balance to pay rent.
- ///
- /// This can be returned from [`Pallet::claim_surcharge`] because the target
- /// contract has enough balance to pay for its rent.
- ContractNotEvictable,
- /// A storage modification exhausted the 32bit type that holds the storage size.
- ///
- /// This can either happen when the accumulated storage in bytes is too large or
- /// when number of storage items is too large.
- StorageExhausted,
- /// A contract with the same AccountId already exists.
- DuplicateContract,
- }
-
- /// Current cost schedule for contracts.
- #[pallet::storage]
- pub(crate) type CurrentSchedule<T: Config> = StorageValue<_, Schedule<T>, ValueQuery>;
-
- /// A mapping from an original code hash to the original code, untouched by instrumentation.
- #[pallet::storage]
- pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;
-
- /// A mapping between an original code hash and instrumented wasm code, ready for execution.
- #[pallet::storage]
- pub(crate) type CodeStorage<T: Config> =
- StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;
-
- /// The subtrie counter.
- #[pallet::storage]
- pub(crate) type AccountCounter<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_initialize`.
- #[pallet::storage]
- pub(crate) type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;
-
- #[pallet::genesis_config]
- pub struct GenesisConfig<T: Config> {
- #[doc = "Current cost schedule for contracts."]
- pub current_schedule: Schedule<T>,
- }
-
- #[cfg(feature = "std")]
- impl<T: Config> Default for GenesisConfig<T> {
- fn default() -> Self {
- Self {
- current_schedule: Default::default(),
- }
- }
- }
-
- #[pallet::genesis_build]
- impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
- fn build(&self) {
- <CurrentSchedule<T>>::put(&self.current_schedule);
- }
- }
-}
-
-impl<T: Config> Pallet<T>
-where
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Perform a call to a specified contract.
- ///
- /// This function is similar to [`Self::call`], but doesn't perform any address lookups
- /// and better suitable for calling directly from Rust.
- ///
- /// It returns the execution result and the amount of used weight.
- pub fn bare_call(
- origin: T::AccountId,
- dest: T::AccountId,
- value: BalanceOf<T>,
- gas_limit: Weight,
- input_data: Vec<u8>,
- ) -> ContractExecResult {
- let mut gas_meter = GasMeter::new(gas_limit);
- let schedule = <CurrentSchedule<T>>::get();
- let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
- let result = ctx.call(dest, value, &mut gas_meter, input_data);
- let gas_consumed = gas_meter.gas_spent();
- ContractExecResult {
- result: result.map(|r| r.0).map_err(|r| r.0.error),
- gas_consumed,
- debug_message: Bytes(Vec::new()).to_vec(),
- }
- }
-
- /// Instantiate a new contract.
- ///
- /// This function is similar to [`Self::instantiate`], but doesn't perform any address lookups
- /// and better suitable for calling directly from Rust.
- ///
- /// It returns the execution result, account id and the amount of used weight.
- ///
- /// If `compute_projection` is set to `true` the result also contains the rent projection.
- /// This is optional because some non trivial and stateful work is performed to compute
- /// the projection. See [`Self::rent_projection`].
- pub fn bare_instantiate(
- origin: T::AccountId,
- endowment: BalanceOf<T>,
- gas_limit: Weight,
- code: Code<CodeHash<T>>,
- data: Vec<u8>,
- salt: Vec<u8>,
- compute_projection: bool,
- ) -> ContractInstantiateResult<T::AccountId, T::BlockNumber> {
- let mut gas_meter = GasMeter::new(gas_limit);
- let schedule = <CurrentSchedule<T>>::get();
- let mut ctx = ExecutionContext::<T, PrefabWasmModule<T>>::top_level(origin, &schedule);
- let executable = match code {
- Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),
- Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),
- };
- let executable = match executable {
- Ok(executable) => executable,
- Err(error) => {
- return ContractInstantiateResult {
- result: Err(error.into()),
- gas_consumed: gas_meter.gas_spent(),
- debug_message: Bytes(Vec::new()).to_vec(),
- }
- }
- };
- let result = ctx
- .instantiate(endowment, &mut gas_meter, executable, data, &salt)
- .and_then(|(account_id, result)| {
- let rent_projection = if compute_projection {
- Some(
- Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
- .map_err(|_| <Error<T>>::NewContractNotFunded)?,
- )
- } else {
- None
- };
-
- Ok(InstantiateReturnValue {
- result,
- account_id,
- rent_projection,
- })
- });
- ContractInstantiateResult {
- result: result.map_err(|e| e.error),
- gas_consumed: gas_meter.gas_spent(),
- debug_message: Bytes(Vec::new()).to_vec(),
- }
- }
-
- /// Query storage of a specified contract under a specified key.
- pub fn get_storage(address: T::AccountId, key: [u8; 32]) -> GetStorageResult {
- let contract_info = ContractInfoOf::<T>::get(&address)
- .ok_or(ContractAccessError::DoesntExist)?
- .get_alive()
- .ok_or(ContractAccessError::IsTombstone)?;
-
- let maybe_value = Storage::<T>::read(&contract_info.trie_id, &key);
- Ok(maybe_value)
- }
-
- /// Query how many blocks the contract stays alive given that the amount endowment
- /// and consumed storage does not change.
- pub fn rent_projection(address: T::AccountId) -> RentProjectionResult<T::BlockNumber> {
- Rent::<T, PrefabWasmModule<T>>::compute_projection(&address)
- }
-
- /// Determine the address of a contract,
- ///
- /// This is the address generation function used by contract instantiation. Its result
- /// is only dependend on its inputs. It can therefore be used to reliably predict the
- /// address of a contract. This is akin to the formular of eth's CREATE2 opcode. There
- /// is no CREATE equivalent because CREATE2 is strictly more powerful.
- ///
- /// Formula: `hash(deploying_address ++ code_hash ++ salt)`
- pub fn contract_address(
- deploying_address: &T::AccountId,
- code_hash: &CodeHash<T>,
- salt: &[u8],
- ) -> T::AccountId {
- let buf: Vec<_> = deploying_address
- .as_ref()
- .iter()
- .chain(code_hash.as_ref())
- .chain(salt)
- .cloned()
- .collect();
- UncheckedFrom::unchecked_from(T::Hashing::hash(&buf))
- }
-
- /// Subsistence threshold is the extension of the minimum balance (aka existential deposit)
- /// by the tombstone deposit, required for leaving a tombstone.
- ///
- /// Rent or any contract initiated balance transfer mechanism cannot make the balance lower
- /// than the subsistence threshold in order to guarantee that a tombstone is created.
- ///
- /// The only way to completely kill a contract without a tombstone is calling `seal_terminate`.
- pub fn subsistence_threshold() -> BalanceOf<T> {
- T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get())
- }
-
- /// The in-memory size in bytes of the data structure associated with each contract.
- ///
- /// The data structure is also put into storage for each contract. The in-storage size
- /// is never larger than the in-memory representation and usually smaller due to compact
- /// encoding and lack of padding.
- ///
- /// # Note
- ///
- /// This returns the in-memory size because the in-storage size (SCALE encoded) cannot
- /// be efficiently determined. Treat this as an upper bound of the in-storage size.
- pub fn contract_info_size() -> u32 {
- sp_std::mem::size_of::<ContractInfo<T>>() as u32
- }
-
- /// Store code for benchmarks which does not check nor instrument the code.
- #[cfg(feature = "runtime-benchmarks")]
- fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {
- let schedule = <CurrentSchedule<T>>::get();
- PrefabWasmModule::store_code_unchecked(code, &schedule)?;
- Ok(())
- }
-
- /// This exists so that benchmarks can determine the weight of running an instrumentation.
- #[cfg(feature = "runtime-benchmarks")]
- fn reinstrument_module(
- module: &mut PrefabWasmModule<T>,
- schedule: &Schedule<T>,
- ) -> frame_support::dispatch::DispatchResult {
- self::wasm::reinstrument(module, schedule)
- }
-}
pallets/contracts/src/migration.rsdiffbeforeafterboth--- a/pallets/contracts/src/migration.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-use crate::{Config, Weight, CurrentSchedule, Pallet, Schedule};
-use frame_support::traits::{GetPalletVersion, PalletVersion, Get};
-
-pub fn migrate<T: Config>() -> Weight {
- let mut weight: Weight = 0;
-
- match <Pallet<T>>::storage_version() {
- // Replace the schedule with the new default and increment its version.
- Some(version) if version == PalletVersion::new(3, 0, 0) => {
- weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
- let _ = <CurrentSchedule<T>>::translate::<u32, _>(|version| {
- version.map(|version| Schedule {
- version: version.saturating_add(1),
- // Default limits were not decreased. Therefore it is OK to overwrite
- // the schedule with the new defaults.
- ..Default::default()
- })
- });
- }
- _ => (),
- }
-
- weight
-}
pallets/contracts/src/rent.rsdiffbeforeafterboth--- a/pallets/contracts/src/rent.rs
+++ /dev/null
@@ -1,553 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-//! A module responsible for computing the right amount of weight and charging it.
-
-use crate::{
- AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Pallet, Event,
- TombstoneContractInfo, Config, CodeHash, Error, storage::Storage, wasm::PrefabWasmModule,
- exec::Executable,
-};
-use sp_std::prelude::*;
-use sp_io::hashing::blake2_256;
-use sp_core::crypto::UncheckedFrom;
-use frame_support::{
- storage::child,
- traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReasons},
-};
-use pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult};
-use sp_runtime::{
- DispatchError,
- traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero},
-};
-
-/// The amount to charge.
-///
-/// This amount respects the contract's rent allowance and the subsistence deposit.
-/// Because of that, charging the amount cannot remove the contract.
-struct OutstandingAmount<T: Config> {
- amount: BalanceOf<T>,
-}
-
-impl<T: Config> OutstandingAmount<T> {
- /// Create the new outstanding amount.
- ///
- /// The amount should be always withdrawable and it should not kill the account.
- fn new(amount: BalanceOf<T>) -> Self {
- Self { amount }
- }
-
- /// Returns the amount this instance wraps.
- fn peek(&self) -> BalanceOf<T> {
- self.amount
- }
-
- /// Withdraws the outstanding amount from the given account.
- fn withdraw(self, account: &T::AccountId) {
- if let Ok(imbalance) = T::Currency::withdraw(
- account,
- self.amount,
- WithdrawReasons::FEE,
- ExistenceRequirement::KeepAlive,
- ) {
- // This should never fail. However, let's err on the safe side.
- T::RentPayment::on_unbalanced(imbalance);
- }
- }
-}
-
-enum Verdict<T: Config> {
- /// The contract is exempted from paying rent.
- ///
- /// For example, it already paid its rent in the current block, or it has enough deposit for not
- /// paying rent at all.
- Exempt,
- /// The contract cannot afford payment within its rent budget so it gets evicted. However,
- /// because its balance is greater than the subsistence threshold it leaves a tombstone.
- Evict {
- amount: Option<OutstandingAmount<T>>,
- },
- /// Everything is OK, we just only take some charge.
- Charge { amount: OutstandingAmount<T> },
-}
-
-pub struct Rent<T, E>(sp_std::marker::PhantomData<(T, E)>);
-
-impl<T, E> Rent<T, E>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
- E: Executable<T>,
-{
- /// Returns a fee charged per block from the contract.
- ///
- /// This function accounts for the storage rent deposit. I.e. if the contract possesses enough funds
- /// then the fee can drop to zero.
- fn compute_fee_per_block(
- free_balance: &BalanceOf<T>,
- contract: &AliveContractInfo<T>,
- code_size_share: u32,
- ) -> BalanceOf<T> {
- let uncovered_by_balance = T::DepositPerStorageByte::get()
- .saturating_mul(contract.storage_size.saturating_add(code_size_share).into())
- .saturating_add(
- T::DepositPerStorageItem::get().saturating_mul(contract.pair_count.into()),
- )
- .saturating_add(T::DepositPerContract::get())
- .saturating_sub(*free_balance);
- T::RentFraction::get().mul_ceil(uncovered_by_balance)
- }
-
- /// Returns amount of funds available to consume by rent mechanism.
- ///
- /// Rent mechanism cannot consume more than `rent_allowance` set by the contract and it cannot make
- /// the balance lower than [`subsistence_threshold`].
- ///
- /// In case the toal_balance is below the subsistence threshold, this function returns `None`.
- fn rent_budget(
- total_balance: &BalanceOf<T>,
- free_balance: &BalanceOf<T>,
- contract: &AliveContractInfo<T>,
- ) -> Option<BalanceOf<T>> {
- let subsistence_threshold = Pallet::<T>::subsistence_threshold();
- // Reserved balance contributes towards the subsistence threshold to stay consistent
- // with the existential deposit where the reserved balance is also counted.
- if *total_balance < subsistence_threshold {
- return None;
- }
-
- // However, reserved balance cannot be charged so we need to use the free balance
- // to calculate the actual budget (which can be 0).
- let rent_allowed_to_charge = free_balance.saturating_sub(subsistence_threshold);
- Some(<BalanceOf<T>>::min(
- contract.rent_allowance,
- rent_allowed_to_charge,
- ))
- }
-
- /// Consider the case for rent payment of the given account and returns a `Verdict`.
- ///
- /// Use `handicap` in case you want to change the reference block number. (To get more details see
- /// `try_eviction` ).
- fn consider_case(
- account: &T::AccountId,
- current_block_number: T::BlockNumber,
- handicap: T::BlockNumber,
- contract: &AliveContractInfo<T>,
- code_size: u32,
- ) -> Verdict<T> {
- // How much block has passed since the last deduction for the contract.
- let blocks_passed = {
- // Calculate an effective block number, i.e. after adjusting for handicap.
- let effective_block_number = current_block_number.saturating_sub(handicap);
- effective_block_number.saturating_sub(contract.deduct_block)
- };
- if blocks_passed.is_zero() {
- // Rent has already been paid
- return Verdict::Exempt;
- }
-
- let total_balance = T::Currency::total_balance(account);
- let free_balance = T::Currency::free_balance(account);
-
- // An amount of funds to charge per block for storage taken up by the contract.
- let fee_per_block = Self::compute_fee_per_block(&free_balance, contract, code_size);
- if fee_per_block.is_zero() {
- // The rent deposit offset reduced the fee to 0. This means that the contract
- // gets the rent for free.
- return Verdict::Exempt;
- }
-
- let rent_budget = match Self::rent_budget(&total_balance, &free_balance, contract) {
- Some(rent_budget) => rent_budget,
- None => {
- // All functions that allow a contract to transfer balance enforce
- // that the contract always stays above the subsistence threshold.
- // We want the rent system to always leave a tombstone to prevent the
- // accidental loss of a contract. Ony `seal_terminate` can remove a
- // contract without a tombstone. Therefore this case should be never
- // hit.
- log::error!(
- target: "runtime::contracts",
- "Tombstoned a contract that is below the subsistence threshold: {:?}",
- account,
- );
- 0u32.into()
- }
- };
-
- let dues = fee_per_block
- .checked_mul(&blocks_passed.saturated_into::<u32>().into())
- .unwrap_or_else(|| <BalanceOf<T>>::max_value());
- let insufficient_rent = rent_budget < dues;
-
- // If the rent payment cannot be withdrawn due to locks on the account balance, then evict the
- // account.
- //
- // NOTE: This seems problematic because it provides a way to tombstone an account while
- // avoiding the last rent payment. In effect, someone could retroactively set rent_allowance
- // for their contract to 0.
- let dues_limited = dues.min(rent_budget);
- let can_withdraw_rent = T::Currency::ensure_can_withdraw(
- account,
- dues_limited,
- WithdrawReasons::FEE,
- free_balance.saturating_sub(dues_limited),
- )
- .is_ok();
-
- if insufficient_rent || !can_withdraw_rent {
- // The contract cannot afford the rent payment and has a balance above the subsistence
- // threshold, so it leaves a tombstone.
- let amount = if can_withdraw_rent {
- Some(OutstandingAmount::new(dues_limited))
- } else {
- None
- };
- return Verdict::Evict { amount };
- }
-
- return Verdict::Charge {
- // We choose to use `dues_limited` here instead of `dues` just to err on the safer side.
- amount: OutstandingAmount::new(dues_limited),
- };
- }
-
- /// Enacts the given verdict and returns the updated `ContractInfo`.
- ///
- /// `alive_contract_info` should be from the same address as `account`.
- ///
- /// # Note
- ///
- /// if `evictable_code` is `None` an `Evict` verdict will not be enacted. This is for
- /// when calling this function during a `call` where access to the soon to be evicted
- /// contract should be denied but storage should be left unmodified.
- fn enact_verdict(
- account: &T::AccountId,
- alive_contract_info: AliveContractInfo<T>,
- current_block_number: T::BlockNumber,
- verdict: Verdict<T>,
- evictable_code: Option<PrefabWasmModule<T>>,
- ) -> Result<Option<AliveContractInfo<T>>, DispatchError> {
- match (verdict, evictable_code) {
- (Verdict::Evict { amount }, Some(code)) => {
- // We need to remove the trie first because it is the only operation
- // that can fail and this function is called without a storage
- // transaction when called through `claim_surcharge`.
- Storage::<T>::queue_trie_for_deletion(&alive_contract_info)?;
-
- if let Some(amount) = amount {
- amount.withdraw(account);
- }
-
- // Note: this operation is heavy.
- let child_storage_root = child::root(&alive_contract_info.child_trie_info());
-
- let tombstone = <TombstoneContractInfo<T>>::new(
- &child_storage_root[..],
- alive_contract_info.code_hash,
- );
- let tombstone_info = ContractInfo::Tombstone(tombstone);
- <ContractInfoOf<T>>::insert(account, &tombstone_info);
- code.drop_from_storage();
- <Pallet<T>>::deposit_event(Event::Evicted(account.clone()));
- Ok(None)
- }
- (Verdict::Evict { amount: _ }, None) => Ok(None),
- (Verdict::Exempt, _) => {
- let contract = ContractInfo::Alive(AliveContractInfo::<T> {
- deduct_block: current_block_number,
- ..alive_contract_info
- });
- <ContractInfoOf<T>>::insert(account, &contract);
- Ok(Some(
- contract
- .get_alive()
- .expect("We just constructed it as alive. qed"),
- ))
- }
- (Verdict::Charge { amount }, _) => {
- let contract = ContractInfo::Alive(AliveContractInfo::<T> {
- rent_allowance: alive_contract_info.rent_allowance - amount.peek(),
- deduct_block: current_block_number,
- rent_payed: alive_contract_info.rent_payed.saturating_add(amount.peek()),
- ..alive_contract_info
- });
- <ContractInfoOf<T>>::insert(account, &contract);
- amount.withdraw(account);
- Ok(Some(
- contract
- .get_alive()
- .expect("We just constructed it as alive. qed"),
- ))
- }
- }
- }
-
- /// Make account paying the rent for the current block number
- ///
- /// This functions does **not** evict the contract. It returns `None` in case the
- /// contract is in need of eviction. [`try_eviction`] must
- /// be called to perform the eviction.
- pub fn charge(
- account: &T::AccountId,
- contract: AliveContractInfo<T>,
- code_size: u32,
- ) -> Result<Option<AliveContractInfo<T>>, DispatchError> {
- let current_block_number = <frame_system::Pallet<T>>::block_number();
- let verdict = Self::consider_case(
- account,
- current_block_number,
- Zero::zero(),
- &contract,
- code_size,
- );
- Self::enact_verdict(account, contract, current_block_number, verdict, None)
- }
-
- /// Process a report that a contract under the given address should be evicted.
- ///
- /// Enact the eviction right away if the contract should be evicted and return the amount
- /// of rent that the contract payed over its lifetime.
- /// Otherwise, **do nothing** and return None.
- ///
- /// The `handicap` parameter gives a way to check the rent to a moment in the past instead
- /// of current block. E.g. if the contract is going to be evicted at the current block,
- /// `handicap = 1` can defer the eviction for 1 block. This is useful to handicap certain snitchers
- /// relative to others.
- ///
- /// NOTE this function performs eviction eagerly. All changes are read and written directly to
- /// storage.
- pub fn try_eviction(
- account: &T::AccountId,
- handicap: T::BlockNumber,
- ) -> Result<(Option<BalanceOf<T>>, u32), DispatchError> {
- let contract = <ContractInfoOf<T>>::get(account);
- let contract = match contract {
- None | Some(ContractInfo::Tombstone(_)) => return Ok((None, 0)),
- Some(ContractInfo::Alive(contract)) => contract,
- };
- let module = PrefabWasmModule::<T>::from_storage_noinstr(contract.code_hash)?;
- let code_len = module.code_len();
- let current_block_number = <frame_system::Pallet<T>>::block_number();
- let verdict = Self::consider_case(
- account,
- current_block_number,
- handicap,
- &contract,
- module.occupied_storage(),
- );
-
- // Enact the verdict only if the contract gets removed.
- match verdict {
- Verdict::Evict { ref amount } => {
- // The outstanding `amount` is withdrawn inside `enact_verdict`.
- let rent_payed = amount
- .as_ref()
- .map(|a| a.peek())
- .unwrap_or_else(|| <BalanceOf<T>>::zero())
- .saturating_add(contract.rent_payed);
- Self::enact_verdict(
- account,
- contract,
- current_block_number,
- verdict,
- Some(module),
- )?;
- Ok((Some(rent_payed), code_len))
- }
- _ => Ok((None, code_len)),
- }
- }
-
- /// Returns the projected time a given contract will be able to sustain paying its rent. The
- /// returned projection is relevant for the current block, i.e. it is as if the contract was
- /// accessed at the beginning of the current block. Returns `None` in case if the contract was
- /// evicted before or as a result of the rent collection.
- ///
- /// The returned value is only an estimation. It doesn't take into account any top ups, changing the
- /// rent allowance, or any problems coming from withdrawing the dues.
- ///
- /// NOTE that this is not a side-effect free function! It will actually collect rent and then
- /// compute the projection. This function is only used for implementation of an RPC method through
- /// `RuntimeApi` meaning that the changes will be discarded anyway.
- pub fn compute_projection(account: &T::AccountId) -> RentProjectionResult<T::BlockNumber> {
- use ContractAccessError::IsTombstone;
-
- let contract_info = <ContractInfoOf<T>>::get(account);
- let alive_contract_info = match contract_info {
- None | Some(ContractInfo::Tombstone(_)) => return Err(IsTombstone),
- Some(ContractInfo::Alive(contract)) => contract,
- };
- let module = <PrefabWasmModule<T>>::from_storage_noinstr(alive_contract_info.code_hash)
- .map_err(|_| IsTombstone)?;
- let code_size = module.occupied_storage();
- let current_block_number = <frame_system::Pallet<T>>::block_number();
- let verdict = Self::consider_case(
- account,
- current_block_number,
- Zero::zero(),
- &alive_contract_info,
- code_size,
- );
-
- // We skip the eviction in case one is in order.
- // Evictions should only be performed by [`try_eviction`].
- let new_contract_info = Self::enact_verdict(
- account,
- alive_contract_info,
- current_block_number,
- verdict,
- None,
- );
-
- // Check what happened after enaction of the verdict.
- let alive_contract_info = new_contract_info
- .map_err(|_| IsTombstone)?
- .ok_or_else(|| IsTombstone)?;
-
- // Compute how much would the fee per block be with the *updated* balance.
- let total_balance = T::Currency::total_balance(account);
- let free_balance = T::Currency::free_balance(account);
- let fee_per_block =
- Self::compute_fee_per_block(&free_balance, &alive_contract_info, code_size);
- if fee_per_block.is_zero() {
- return Ok(RentProjection::NoEviction);
- }
-
- // Then compute how much the contract will sustain under these circumstances.
- let rent_budget = Self::rent_budget(&total_balance, &free_balance, &alive_contract_info)
- .expect(
- "the contract exists and in the alive state;
- the updated balance must be greater than subsistence deposit;
- this function doesn't return `None`;
- qed
- ",
- );
- let blocks_left = match rent_budget.checked_div(&fee_per_block) {
- Some(blocks_left) => blocks_left,
- None => {
- // `fee_per_block` is not zero here, so `checked_div` can return `None` if
- // there is an overflow. This cannot happen with integers though. Return
- // `NoEviction` here just in case.
- return Ok(RentProjection::NoEviction);
- }
- };
-
- let blocks_left = blocks_left.saturated_into::<u32>().into();
- Ok(RentProjection::EvictionAt(
- current_block_number + blocks_left,
- ))
- }
-
- /// Restores the destination account using the origin as prototype.
- ///
- /// The restoration will be performed iff:
- /// - the supplied code_hash does still exist on-chain
- /// - origin exists and is alive,
- /// - the origin's storage is not written in the current block
- /// - the restored account has tombstone
- /// - the tombstone matches the hash of the origin storage root, and code hash.
- ///
- /// Upon succesful restoration, `origin` will be destroyed, all its funds are transferred to
- /// the restored account. The restored account will inherit the last write block and its last
- /// deduct block will be set to the current block.
- ///
- /// # Return Value
- ///
- /// Result<(CallerCodeSize, DestCodeSize), (DispatchError, CallerCodeSize, DestCodesize)>
- pub fn restore_to(
- origin: T::AccountId,
- dest: T::AccountId,
- code_hash: CodeHash<T>,
- rent_allowance: BalanceOf<T>,
- delta: Vec<crate::exec::StorageKey>,
- ) -> Result<(u32, u32), (DispatchError, u32, u32)> {
- let mut origin_contract = <ContractInfoOf<T>>::get(&origin)
- .and_then(|c| c.get_alive())
- .ok_or((Error::<T>::InvalidSourceContract.into(), 0, 0))?;
-
- let child_trie_info = origin_contract.child_trie_info();
-
- let current_block = <frame_system::Pallet<T>>::block_number();
-
- if origin_contract.last_write == Some(current_block) {
- return Err((Error::<T>::InvalidContractOrigin.into(), 0, 0));
- }
-
- let dest_tombstone = <ContractInfoOf<T>>::get(&dest)
- .and_then(|c| c.get_tombstone())
- .ok_or((Error::<T>::InvalidDestinationContract.into(), 0, 0))?;
-
- let last_write = if !delta.is_empty() {
- Some(current_block)
- } else {
- origin_contract.last_write
- };
-
- // Fails if the code hash does not exist on chain
- let caller_code_len = E::add_user(code_hash).map_err(|e| (e, 0, 0))?;
-
- // We are allowed to eagerly modify storage even though the function can
- // fail later due to tombstones not matching. This is because the restoration
- // is always called from a contract and therefore in a storage transaction.
- // The failure of this function will lead to this transaction's rollback.
- let bytes_taken: u32 = delta
- .iter()
- .filter_map(|key| {
- let key = blake2_256(key);
- child::get_raw(&child_trie_info, &key).map(|value| {
- child::kill(&child_trie_info, &key);
- value.len() as u32
- })
- })
- .sum();
-
- let tombstone = <TombstoneContractInfo<T>>::new(
- // This operation is cheap enough because last_write (delta not included)
- // is not this block as it has been checked earlier.
- &child::root(&child_trie_info)[..],
- code_hash,
- );
-
- if tombstone != dest_tombstone {
- return Err((Error::<T>::InvalidTombstone.into(), caller_code_len, 0));
- }
-
- origin_contract.storage_size -= bytes_taken;
-
- <ContractInfoOf<T>>::remove(&origin);
- let tombstone_code_len = E::remove_user(origin_contract.code_hash);
- <ContractInfoOf<T>>::insert(
- &dest,
- ContractInfo::Alive(AliveContractInfo::<T> {
- code_hash,
- rent_allowance,
- rent_payed: <BalanceOf<T>>::zero(),
- deduct_block: current_block,
- last_write,
- ..origin_contract
- }),
- );
-
- let origin_free_balance = T::Currency::free_balance(&origin);
- T::Currency::make_free_balance_be(&origin, <BalanceOf<T>>::zero());
- T::Currency::deposit_creating(&dest, origin_free_balance);
-
- Ok((caller_code_len, tombstone_code_len))
- }
-}
pallets/contracts/src/schedule.rsdiffbeforeafterboth--- a/pallets/contracts/src/schedule.rs
+++ /dev/null
@@ -1,806 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2020-2021 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.
-
-//! This module contains the cost schedule and supporting code that constructs a
-//! sane default schedule from a `WeightInfo` implementation.
-
-use crate::{Config, weights::WeightInfo};
-
-#[cfg(feature = "std")]
-use serde::{Serialize, Deserialize};
-use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug};
-use frame_support::weights::Weight;
-use sp_std::{marker::PhantomData, vec::Vec};
-use codec::{Encode, Decode};
-use parity_wasm::elements;
-use pwasm_utils::rules;
-use sp_runtime::RuntimeDebug;
-
-/// How many API calls are executed in a single batch. The reason for increasing the amount
-/// of API calls in batches (per benchmark component increase) is so that the linear regression
-/// has an easier time determining the contribution of that component.
-pub const API_BENCHMARK_BATCH_SIZE: u32 = 100;
-
-/// How many instructions are executed in a single batch. The reasoning is the same
-/// as for `API_BENCHMARK_BATCH_SIZE`.
-pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
-
-/// Definition of the cost schedule and other parameterizations for the wasm vm.
-///
-/// Its fields are private to the crate in order to allow addition of new contract
-/// callable functions without bumping to a new major version. A genesis config should
-/// rely on public functions of this type.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
-#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
-pub struct Schedule<T: Config> {
- /// Version of the schedule.
- ///
- /// # Note
- ///
- /// Must be incremented whenever the [`self.instruction_weights`] are changed. The
- /// reason is that changes to instruction weights require a re-instrumentation
- /// of all contracts which are triggered by a version comparison on call.
- /// Changes to other parts of the schedule should not increment the version in
- /// order to avoid unnecessary re-instrumentations.
- pub(crate) version: u32,
-
- /// Whether the `seal_println` function is allowed to be used contracts.
- /// MUST only be enabled for `dev` chains, NOT for production chains
- pub(crate) enable_println: bool,
-
- /// Describes the upper limits on various metrics.
- pub(crate) limits: Limits,
-
- /// The weights for individual wasm instructions.
- pub(crate) instruction_weights: InstructionWeights<T>,
-
- /// The weights for each imported function a contract is allowed to call.
- pub(crate) host_fn_weights: HostFnWeights<T>,
-}
-
-/// Describes the upper limits on various metrics.
-///
-/// # Note
-///
-/// The values in this struct should only ever be increased for a deployed chain. The reason
-/// is that decreasing those values will break existing contracts which are above the new limits.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
-pub struct Limits {
- /// The maximum number of topics supported by an event.
- pub event_topics: u32,
-
- /// Maximum allowed stack height in number of elements.
- ///
- /// See <https://wiki.parity.io/WebAssembly-StackHeight> to find out
- /// how the stack frame cost is calculated. Each element can be of one of the
- /// wasm value types. This means the maximum size per element is 64bit.
- pub stack_height: u32,
-
- /// Maximum number of globals a module is allowed to declare.
- ///
- /// Globals are not limited through the `stack_height` as locals are. Neither does
- /// the linear memory limit `memory_pages` applies to them.
- pub globals: u32,
-
- /// Maximum numbers of parameters a function can have.
- ///
- /// Those need to be limited to prevent a potentially exploitable interaction with
- /// the stack height instrumentation: The costs of executing the stack height
- /// instrumentation for an indirectly called function scales linearly with the amount
- /// of parameters of this function. Because the stack height instrumentation itself is
- /// is not weight metered its costs must be static (via this limit) and included in
- /// the costs of the instructions that cause them (call, call_indirect).
- pub parameters: u32,
-
- /// Maximum number of memory pages allowed for a contract.
- pub memory_pages: u32,
-
- /// Maximum number of elements allowed in a table.
- ///
- /// Currently, the only type of element that is allowed in a table is funcref.
- pub table_size: u32,
-
- /// Maximum number of elements that can appear as immediate value to the br_table instruction.
- pub br_table_size: u32,
-
- /// The maximum length of a subject in bytes used for PRNG generation.
- pub subject_len: u32,
-}
-
-impl Limits {
- /// The maximum memory size in bytes that a contract can occupy.
- pub fn max_memory_size(&self) -> u32 {
- self.memory_pages * 64 * 1024
- }
-}
-
-/// Describes the weight for all categories of supported wasm instructions.
-///
-/// There there is one field for each wasm instruction that describes the weight to
-/// execute one instruction of that name. There are a few execptions:
-///
-/// 1. If there is a i64 and a i32 variant of an instruction we use the weight
-/// of the former for both.
-/// 2. The following instructions are free of charge because they merely structure the
-/// wasm module and cannot be spammed without making the module invalid (and rejected):
-/// End, Unreachable, Return, Else
-/// 3. The following instructions cannot be benchmarked because they are removed by any
-/// real world execution engine as a preprocessing step and therefore don't yield a
-/// meaningful benchmark result. However, in contrast to the instructions mentioned
-/// in 2. they can be spammed. We price them with the same weight as the "default"
-/// instruction (i64.const): Block, Loop, Nop
-/// 4. We price both i64.const and drop as InstructionWeights.i64const / 2. The reason
-/// for that is that we cannot benchmark either of them on its own but we need their
-/// individual values to derive (by subtraction) the weight of all other instructions
-/// that use them as supporting instructions. Supporting means mainly pushing arguments
-/// and dropping return values in order to maintain a valid module.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
-pub struct InstructionWeights<T: Config> {
- pub i64const: u32,
- pub i64load: u32,
- pub i64store: u32,
- pub select: u32,
- pub r#if: u32,
- pub br: u32,
- pub br_if: u32,
- pub br_table: u32,
- pub br_table_per_entry: u32,
- pub call: u32,
- pub call_indirect: u32,
- pub call_indirect_per_param: u32,
- pub local_get: u32,
- pub local_set: u32,
- pub local_tee: u32,
- pub global_get: u32,
- pub global_set: u32,
- pub memory_current: u32,
- pub memory_grow: u32,
- pub i64clz: u32,
- pub i64ctz: u32,
- pub i64popcnt: u32,
- pub i64eqz: u32,
- pub i64extendsi32: u32,
- pub i64extendui32: u32,
- pub i32wrapi64: u32,
- pub i64eq: u32,
- pub i64ne: u32,
- pub i64lts: u32,
- pub i64ltu: u32,
- pub i64gts: u32,
- pub i64gtu: u32,
- pub i64les: u32,
- pub i64leu: u32,
- pub i64ges: u32,
- pub i64geu: u32,
- pub i64add: u32,
- pub i64sub: u32,
- pub i64mul: u32,
- pub i64divs: u32,
- pub i64divu: u32,
- pub i64rems: u32,
- pub i64remu: u32,
- pub i64and: u32,
- pub i64or: u32,
- pub i64xor: u32,
- pub i64shl: u32,
- pub i64shrs: u32,
- pub i64shru: u32,
- pub i64rotl: u32,
- pub i64rotr: u32,
- /// The type parameter is used in the default implementation.
- #[codec(skip)]
- pub _phantom: PhantomData<T>,
-}
-
-/// Describes the weight for each imported function that a contract is allowed to call.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
-pub struct HostFnWeights<T: Config> {
- /// Weight of calling `seal_caller`.
- pub caller: Weight,
-
- /// Weight of calling `seal_address`.
- pub address: Weight,
-
- /// Weight of calling `seal_gas_left`.
- pub gas_left: Weight,
-
- /// Weight of calling `seal_balance`.
- pub balance: Weight,
-
- /// Weight of calling `seal_value_transferred`.
- pub value_transferred: Weight,
-
- /// Weight of calling `seal_minimum_balance`.
- pub minimum_balance: Weight,
-
- /// Weight of calling `seal_tombstone_deposit`.
- pub tombstone_deposit: Weight,
-
- /// Weight of calling `seal_rent_allowance`.
- pub rent_allowance: Weight,
-
- /// Weight of calling `seal_block_number`.
- pub block_number: Weight,
-
- /// Weight of calling `seal_now`.
- pub now: Weight,
-
- /// Weight of calling `seal_weight_to_fee`.
- pub weight_to_fee: Weight,
-
- /// Weight of calling `gas`.
- pub gas: Weight,
-
- /// Weight of calling `seal_input`.
- pub input: Weight,
-
- /// Weight per input byte copied to contract memory by `seal_input`.
- pub input_per_byte: Weight,
-
- /// Weight of calling `seal_return`.
- pub r#return: Weight,
-
- /// Weight per byte returned through `seal_return`.
- pub return_per_byte: Weight,
-
- /// Weight of calling `seal_terminate`.
- pub terminate: Weight,
-
- /// Weight per byte of the terminated contract.
- pub terminate_per_code_byte: Weight,
-
- /// Weight of calling `seal_restore_to`.
- pub restore_to: Weight,
-
- /// Weight per byte of the restoring contract.
- pub restore_to_per_caller_code_byte: Weight,
-
- /// Weight per byte of the restored contract.
- pub restore_to_per_tombstone_code_byte: Weight,
-
- /// Weight per delta key supplied to `seal_restore_to`.
- pub restore_to_per_delta: Weight,
-
- /// Weight of calling `seal_random`.
- pub random: Weight,
-
- /// Weight of calling `seal_reposit_event`.
- pub deposit_event: Weight,
-
- /// Weight per topic supplied to `seal_deposit_event`.
- pub deposit_event_per_topic: Weight,
-
- /// Weight per byte of an event deposited through `seal_deposit_event`.
- pub deposit_event_per_byte: Weight,
-
- /// Weight of calling `seal_set_rent_allowance`.
- pub set_rent_allowance: Weight,
-
- /// Weight of calling `seal_set_storage`.
- pub set_storage: Weight,
-
- /// Weight per byte of an item stored with `seal_set_storage`.
- pub set_storage_per_byte: Weight,
-
- /// Weight of calling `seal_clear_storage`.
- pub clear_storage: Weight,
-
- /// Weight of calling `seal_get_storage`.
- pub get_storage: Weight,
-
- /// Weight per byte of an item received via `seal_get_storage`.
- pub get_storage_per_byte: Weight,
-
- /// Weight of calling `seal_transfer`.
- pub transfer: Weight,
-
- /// Weight of calling `seal_call`.
- pub call: Weight,
-
- /// Weight per byte of the called contract.
- pub call_per_code_byte: Weight,
-
- /// Weight surcharge that is claimed if `seal_call` does a balance transfer.
- pub call_transfer_surcharge: Weight,
-
- /// Weight per input byte supplied to `seal_call`.
- pub call_per_input_byte: Weight,
-
- /// Weight per output byte received through `seal_call`.
- pub call_per_output_byte: Weight,
-
- /// Weight of calling `seal_instantiate`.
- pub instantiate: Weight,
-
- /// Weight per byte of the instantiated contract.
- pub instantiate_per_code_byte: Weight,
-
- /// Weight per input byte supplied to `seal_instantiate`.
- pub instantiate_per_input_byte: Weight,
-
- /// Weight per output byte received through `seal_instantiate`.
- pub instantiate_per_output_byte: Weight,
-
- /// Weight per salt byte supplied to `seal_instantiate`.
- pub instantiate_per_salt_byte: Weight,
-
- /// Weight of calling `seal_hash_sha_256`.
- pub hash_sha2_256: Weight,
-
- /// Weight per byte hashed by `seal_hash_sha_256`.
- pub hash_sha2_256_per_byte: Weight,
-
- /// Weight of calling `seal_hash_keccak_256`.
- pub hash_keccak_256: Weight,
-
- /// Weight per byte hashed by `seal_hash_keccak_256`.
- pub hash_keccak_256_per_byte: Weight,
-
- /// Weight of calling `seal_hash_blake2_256`.
- pub hash_blake2_256: Weight,
-
- /// Weight per byte hashed by `seal_hash_blake2_256`.
- pub hash_blake2_256_per_byte: Weight,
-
- /// Weight of calling `seal_hash_blake2_128`.
- pub hash_blake2_128: Weight,
-
- /// Weight per byte hashed by `seal_hash_blake2_128`.
- pub hash_blake2_128_per_byte: Weight,
-
- /// Weight of calling `seal_rent_params`.
- pub rent_params: Weight,
-
- /// The type parameter is used in the default implementation.
- #[codec(skip)]
- pub _phantom: PhantomData<T>,
-}
-
-macro_rules! replace_token {
- ($_in:tt $replacement:tt) => {
- $replacement
- };
-}
-
-macro_rules! call_zero {
- ($name:ident, $( $arg:expr ),*) => {
- T::WeightInfo::$name($( replace_token!($arg 0) ),*)
- };
-}
-
-macro_rules! cost_args {
- ($name:ident, $( $arg: expr ),+) => {
- (T::WeightInfo::$name($( $arg ),+).saturating_sub(call_zero!($name, $( $arg ),+)))
- }
-}
-
-macro_rules! cost_batched_args {
- ($name:ident, $( $arg: expr ),+) => {
- cost_args!($name, $( $arg ),+) / Weight::from(API_BENCHMARK_BATCH_SIZE)
- }
-}
-
-macro_rules! cost_instr_no_params_with_batch_size {
- ($name:ident, $batch_size:expr) => {
- (cost_args!($name, 1) / Weight::from($batch_size)) as u32
- };
-}
-
-macro_rules! cost_instr_with_batch_size {
- ($name:ident, $num_params:expr, $batch_size:expr) => {
- cost_instr_no_params_with_batch_size!($name, $batch_size).saturating_sub(
- (cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2)
- .saturating_mul($num_params),
- )
- };
-}
-
-macro_rules! cost_instr {
- ($name:ident, $num_params:expr) => {
- cost_instr_with_batch_size!($name, $num_params, INSTR_BENCHMARK_BATCH_SIZE)
- };
-}
-
-macro_rules! cost_byte_args {
- ($name:ident, $( $arg: expr ),+) => {
- cost_args!($name, $( $arg ),+) / 1024
- }
-}
-
-macro_rules! cost_byte_batched_args {
- ($name:ident, $( $arg: expr ),+) => {
- cost_batched_args!($name, $( $arg ),+) / 1024
- }
-}
-
-macro_rules! cost {
- ($name:ident) => {
- cost_args!($name, 1)
- };
-}
-
-macro_rules! cost_batched {
- ($name:ident) => {
- cost_batched_args!($name, 1)
- };
-}
-
-macro_rules! cost_byte {
- ($name:ident) => {
- cost_byte_args!($name, 1)
- };
-}
-
-macro_rules! cost_byte_batched {
- ($name:ident) => {
- cost_byte_batched_args!($name, 1)
- };
-}
-
-impl<T: Config> Default for Schedule<T> {
- fn default() -> Self {
- Self {
- version: 0,
- enable_println: false,
- limits: Default::default(),
- instruction_weights: Default::default(),
- host_fn_weights: Default::default(),
- }
- }
-}
-
-impl Default for Limits {
- fn default() -> Self {
- Self {
- event_topics: 4,
- // 512 * sizeof(i64) will give us a 4k stack.
- stack_height: 512,
- globals: 256,
- parameters: 128,
- memory_pages: 16,
- // 4k function pointers (This is in count not bytes).
- table_size: 4096,
- br_table_size: 256,
- subject_len: 32,
- }
- }
-}
-
-impl<T: Config> Default for InstructionWeights<T> {
- fn default() -> Self {
- let max_pages = Limits::default().memory_pages;
- Self {
- i64const: cost_instr!(instr_i64const, 1),
- i64load: cost_instr!(instr_i64load, 2),
- i64store: cost_instr!(instr_i64store, 2),
- select: cost_instr!(instr_select, 4),
- r#if: cost_instr!(instr_if, 3),
- br: cost_instr!(instr_br, 2),
- br_if: cost_instr!(instr_br_if, 5),
- br_table: cost_instr!(instr_br_table, 3),
- br_table_per_entry: cost_instr!(instr_br_table_per_entry, 0),
- call: cost_instr!(instr_call, 2),
- call_indirect: cost_instr!(instr_call_indirect, 3),
- call_indirect_per_param: cost_instr!(instr_call_indirect_per_param, 1),
- local_get: cost_instr!(instr_local_get, 1),
- local_set: cost_instr!(instr_local_set, 1),
- local_tee: cost_instr!(instr_local_tee, 2),
- global_get: cost_instr!(instr_global_get, 1),
- global_set: cost_instr!(instr_global_set, 1),
- memory_current: cost_instr!(instr_memory_current, 1),
- memory_grow: cost_instr_with_batch_size!(instr_memory_grow, 1, max_pages),
- i64clz: cost_instr!(instr_i64clz, 2),
- i64ctz: cost_instr!(instr_i64ctz, 2),
- i64popcnt: cost_instr!(instr_i64popcnt, 2),
- i64eqz: cost_instr!(instr_i64eqz, 2),
- i64extendsi32: cost_instr!(instr_i64extendsi32, 2),
- i64extendui32: cost_instr!(instr_i64extendui32, 2),
- i32wrapi64: cost_instr!(instr_i32wrapi64, 2),
- i64eq: cost_instr!(instr_i64eq, 3),
- i64ne: cost_instr!(instr_i64ne, 3),
- i64lts: cost_instr!(instr_i64lts, 3),
- i64ltu: cost_instr!(instr_i64ltu, 3),
- i64gts: cost_instr!(instr_i64gts, 3),
- i64gtu: cost_instr!(instr_i64gtu, 3),
- i64les: cost_instr!(instr_i64les, 3),
- i64leu: cost_instr!(instr_i64leu, 3),
- i64ges: cost_instr!(instr_i64ges, 3),
- i64geu: cost_instr!(instr_i64geu, 3),
- i64add: cost_instr!(instr_i64add, 3),
- i64sub: cost_instr!(instr_i64sub, 3),
- i64mul: cost_instr!(instr_i64mul, 3),
- i64divs: cost_instr!(instr_i64divs, 3),
- i64divu: cost_instr!(instr_i64divu, 3),
- i64rems: cost_instr!(instr_i64rems, 3),
- i64remu: cost_instr!(instr_i64remu, 3),
- i64and: cost_instr!(instr_i64and, 3),
- i64or: cost_instr!(instr_i64or, 3),
- i64xor: cost_instr!(instr_i64xor, 3),
- i64shl: cost_instr!(instr_i64shl, 3),
- i64shrs: cost_instr!(instr_i64shrs, 3),
- i64shru: cost_instr!(instr_i64shru, 3),
- i64rotl: cost_instr!(instr_i64rotl, 3),
- i64rotr: cost_instr!(instr_i64rotr, 3),
- _phantom: PhantomData,
- }
- }
-}
-
-impl<T: Config> Default for HostFnWeights<T> {
- fn default() -> Self {
- Self {
- caller: cost_batched!(seal_caller),
- address: cost_batched!(seal_address),
- gas_left: cost_batched!(seal_gas_left),
- balance: cost_batched!(seal_balance),
- value_transferred: cost_batched!(seal_value_transferred),
- minimum_balance: cost_batched!(seal_minimum_balance),
- tombstone_deposit: cost_batched!(seal_tombstone_deposit),
- rent_allowance: cost_batched!(seal_rent_allowance),
- block_number: cost_batched!(seal_block_number),
- now: cost_batched!(seal_now),
- weight_to_fee: cost_batched!(seal_weight_to_fee),
- gas: cost_batched!(seal_gas),
- input: cost!(seal_input),
- input_per_byte: cost_byte!(seal_input_per_kb),
- r#return: cost!(seal_return),
- return_per_byte: cost_byte!(seal_return_per_kb),
- terminate: cost!(seal_terminate),
- terminate_per_code_byte: cost_byte!(seal_terminate_per_code_kb),
- restore_to: cost!(seal_restore_to),
- restore_to_per_caller_code_byte: cost_byte_args!(
- seal_restore_to_per_code_kb_delta,
- 1,
- 0,
- 0
- ),
- restore_to_per_tombstone_code_byte: cost_byte_args!(
- seal_restore_to_per_code_kb_delta,
- 0,
- 1,
- 0
- ),
- restore_to_per_delta: cost_batched_args!(seal_restore_to_per_code_kb_delta, 0, 0, 1),
- random: cost_batched!(seal_random),
- deposit_event: cost_batched!(seal_deposit_event),
- deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0),
- deposit_event_per_byte: cost_byte_batched_args!(
- seal_deposit_event_per_topic_and_kb,
- 0,
- 1
- ),
- set_rent_allowance: cost_batched!(seal_set_rent_allowance),
- set_storage: cost_batched!(seal_set_storage),
- set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),
- clear_storage: cost_batched!(seal_clear_storage),
- get_storage: cost_batched!(seal_get_storage),
- get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb),
- transfer: cost_batched!(seal_transfer),
- call: cost_batched!(seal_call),
- call_per_code_byte: cost_byte_batched_args!(
- seal_call_per_code_transfer_input_output_kb,
- 1,
- 0,
- 0,
- 0
- ),
- call_transfer_surcharge: cost_batched_args!(
- seal_call_per_code_transfer_input_output_kb,
- 0,
- 1,
- 0,
- 0
- ),
- call_per_input_byte: cost_byte_batched_args!(
- seal_call_per_code_transfer_input_output_kb,
- 0,
- 0,
- 1,
- 0
- ),
- call_per_output_byte: cost_byte_batched_args!(
- seal_call_per_code_transfer_input_output_kb,
- 0,
- 0,
- 0,
- 1
- ),
- instantiate: cost_batched!(seal_instantiate),
- instantiate_per_code_byte: cost_byte_batched_args!(
- seal_instantiate_per_code_input_output_salt_kb,
- 1,
- 0,
- 0,
- 0
- ),
- instantiate_per_input_byte: cost_byte_batched_args!(
- seal_instantiate_per_code_input_output_salt_kb,
- 0,
- 1,
- 0,
- 0
- ),
- instantiate_per_output_byte: cost_byte_batched_args!(
- seal_instantiate_per_code_input_output_salt_kb,
- 0,
- 0,
- 1,
- 0
- ),
- instantiate_per_salt_byte: cost_byte_batched_args!(
- seal_instantiate_per_code_input_output_salt_kb,
- 0,
- 0,
- 0,
- 1
- ),
- hash_sha2_256: cost_batched!(seal_hash_sha2_256),
- hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb),
- hash_keccak_256: cost_batched!(seal_hash_keccak_256),
- hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb),
- hash_blake2_256: cost_batched!(seal_hash_blake2_256),
- hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb),
- hash_blake2_128: cost_batched!(seal_hash_blake2_128),
- hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb),
- rent_params: cost_batched!(seal_rent_params),
- _phantom: PhantomData,
- }
- }
-}
-
-struct ScheduleRules<'a, T: Config> {
- schedule: &'a Schedule<T>,
- params: Vec<u32>,
-}
-
-impl<T: Config> Schedule<T> {
- /// Allow contracts to call `seal_println` in order to print messages to the console.
- ///
- /// This should only ever be activated in development chains. The printed messages
- /// can be observed on the console by setting the environment variable
- /// `RUST_LOG=runtime=debug` when running the node.
- ///
- /// # Note
- ///
- /// Is set to `false` by default.
- pub fn enable_println(mut self, enable: bool) -> Self {
- self.enable_println = enable;
- self
- }
-
- pub(crate) fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {
- ScheduleRules {
- schedule: &self,
- params: module
- .type_section()
- .iter()
- .flat_map(|section| section.types())
- .map(|func| {
- let elements::Type::Function(func) = func;
- func.params().len() as u32
- })
- .collect(),
- }
- }
-}
-
-impl<'a, T: Config> rules::Rules for ScheduleRules<'a, T> {
- fn instruction_cost(&self, instruction: &elements::Instruction) -> Option<u32> {
- use parity_wasm::elements::Instruction::*;
- let w = &self.schedule.instruction_weights;
- let max_params = self.schedule.limits.parameters;
-
- let weight = match *instruction {
- End | Unreachable | Return | Else => 0,
- I32Const(_) | I64Const(_) | Block(_) | Loop(_) | Nop | Drop => w.i64const,
- I32Load(_, _)
- | I32Load8S(_, _)
- | I32Load8U(_, _)
- | I32Load16S(_, _)
- | I32Load16U(_, _)
- | I64Load(_, _)
- | I64Load8S(_, _)
- | I64Load8U(_, _)
- | I64Load16S(_, _)
- | I64Load16U(_, _)
- | I64Load32S(_, _)
- | I64Load32U(_, _) => w.i64load,
- I32Store(_, _)
- | I32Store8(_, _)
- | I32Store16(_, _)
- | I64Store(_, _)
- | I64Store8(_, _)
- | I64Store16(_, _)
- | I64Store32(_, _) => w.i64store,
- Select => w.select,
- If(_) => w.r#if,
- Br(_) => w.br,
- BrIf(_) => w.br_if,
- Call(_) => w.call,
- GetLocal(_) => w.local_get,
- SetLocal(_) => w.local_set,
- TeeLocal(_) => w.local_tee,
- GetGlobal(_) => w.global_get,
- SetGlobal(_) => w.global_set,
- CurrentMemory(_) => w.memory_current,
- GrowMemory(_) => w.memory_grow,
- CallIndirect(idx, _) => *self.params.get(idx as usize).unwrap_or(&max_params),
- BrTable(ref data) => w
- .br_table
- .saturating_add(w.br_table_per_entry.saturating_mul(data.table.len() as u32)),
- I32Clz | I64Clz => w.i64clz,
- I32Ctz | I64Ctz => w.i64ctz,
- I32Popcnt | I64Popcnt => w.i64popcnt,
- I32Eqz | I64Eqz => w.i64eqz,
- I64ExtendSI32 => w.i64extendsi32,
- I64ExtendUI32 => w.i64extendui32,
- I32WrapI64 => w.i32wrapi64,
- I32Eq | I64Eq => w.i64eq,
- I32Ne | I64Ne => w.i64ne,
- I32LtS | I64LtS => w.i64lts,
- I32LtU | I64LtU => w.i64ltu,
- I32GtS | I64GtS => w.i64gts,
- I32GtU | I64GtU => w.i64gtu,
- I32LeS | I64LeS => w.i64les,
- I32LeU | I64LeU => w.i64leu,
- I32GeS | I64GeS => w.i64ges,
- I32GeU | I64GeU => w.i64geu,
- I32Add | I64Add => w.i64add,
- I32Sub | I64Sub => w.i64sub,
- I32Mul | I64Mul => w.i64mul,
- I32DivS | I64DivS => w.i64divs,
- I32DivU | I64DivU => w.i64divu,
- I32RemS | I64RemS => w.i64rems,
- I32RemU | I64RemU => w.i64remu,
- I32And | I64And => w.i64and,
- I32Or | I64Or => w.i64or,
- I32Xor | I64Xor => w.i64xor,
- I32Shl | I64Shl => w.i64shl,
- I32ShrS | I64ShrS => w.i64shrs,
- I32ShrU | I64ShrU => w.i64shru,
- I32Rotl | I64Rotl => w.i64rotl,
- I32Rotr | I64Rotr => w.i64rotr,
-
- // Returning None makes the gas instrumentation fail which we intend for
- // unsupported or unknown instructions.
- _ => return None,
- };
- Some(weight)
- }
-
- fn memory_grow_cost(&self) -> Option<rules::MemoryGrowCost> {
- // We benchmarked the memory.grow instruction with the maximum allowed pages.
- // The cost for growing is therefore already included in the instruction cost.
- None
- }
-}
-
-#[cfg(test)]
-mod test {
- use crate::tests::Test;
- use super::*;
-
- #[test]
- fn print_test_schedule() {
- let schedule = Schedule::<Test>::default();
- println!("{:#?}", schedule);
- }
-}
pallets/contracts/src/storage.rsdiffbeforeafterboth--- a/pallets/contracts/src/storage.rs
+++ /dev/null
@@ -1,440 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2019-2021 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.
-
-//! This module contains routines for accessing and altering a contract related state.
-
-use crate::{
- exec::{AccountIdOf, StorageKey},
- BalanceOf, CodeHash, ContractInfoOf, Config, TrieId, AccountCounter, DeletionQueue, Error,
- weights::WeightInfo,
-};
-use codec::{Codec, Encode, Decode};
-use sp_std::prelude::*;
-use sp_std::{marker::PhantomData, fmt::Debug};
-use sp_io::hashing::blake2_256;
-use sp_runtime::{
- RuntimeDebug,
- traits::{Bounded, Saturating, Zero, Hash, Member, MaybeSerializeDeserialize},
-};
-use sp_core::crypto::UncheckedFrom;
-use frame_support::{
- dispatch::{DispatchError, DispatchResult},
- storage::child::{self, KillChildStorageResult, ChildInfo},
- traits::Get,
- weights::Weight,
-};
-
-pub type AliveContractInfo<T> =
- RawAliveContractInfo<CodeHash<T>, BalanceOf<T>, <T as frame_system::Config>::BlockNumber>;
-pub type TombstoneContractInfo<T> = RawTombstoneContractInfo<
- <T as frame_system::Config>::Hash,
- <T as frame_system::Config>::Hashing,
->;
-
-/// Information for managing an account and its sub trie abstraction.
-/// This is the required info to cache for an account
-#[derive(Encode, Decode, RuntimeDebug)]
-pub enum ContractInfo<T: Config> {
- Alive(AliveContractInfo<T>),
- Tombstone(TombstoneContractInfo<T>),
-}
-
-impl<T: Config> ContractInfo<T> {
- /// If contract is alive then return some alive info
- pub fn get_alive(self) -> Option<AliveContractInfo<T>> {
- if let ContractInfo::Alive(alive) = self {
- Some(alive)
- } else {
- None
- }
- }
- /// If contract is alive then return some reference to alive info
- pub fn as_alive(&self) -> Option<&AliveContractInfo<T>> {
- if let ContractInfo::Alive(ref alive) = self {
- Some(alive)
- } else {
- None
- }
- }
-
- /// If contract is tombstone then return some tombstone info
- pub fn get_tombstone(self) -> Option<TombstoneContractInfo<T>> {
- if let ContractInfo::Tombstone(tombstone) = self {
- Some(tombstone)
- } else {
- None
- }
- }
-}
-
-/// Information for managing an account and its sub trie abstraction.
-/// This is the required info to cache for an account.
-#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
-pub struct RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
- /// Unique ID for the subtree encoded as a bytes vector.
- pub trie_id: TrieId,
- /// The total number of bytes used by this contract.
- ///
- /// It is a sum of each key-value pair stored by this contract.
- pub storage_size: u32,
- /// The total number of key-value pairs in storage of this contract.
- pub pair_count: u32,
- /// The code associated with a given account.
- pub code_hash: CodeHash,
- /// Pay rent at most up to this value.
- pub rent_allowance: Balance,
- /// The amount of rent that was payed by the contract over its whole lifetime.
- ///
- /// A restored contract starts with a value of zero just like a new contract.
- pub rent_payed: Balance,
- /// Last block rent has been payed.
- pub deduct_block: BlockNumber,
- /// Last block child storage has been written.
- pub last_write: Option<BlockNumber>,
- /// This field is reserved for future evolution of format.
- pub _reserved: Option<()>,
-}
-
-impl<CodeHash, Balance, BlockNumber> RawAliveContractInfo<CodeHash, Balance, BlockNumber> {
- /// Associated child trie unique id is built from the hash part of the trie id.
- pub fn child_trie_info(&self) -> ChildInfo {
- child_trie_info(&self.trie_id[..])
- }
-}
-
-/// Associated child trie unique id is built from the hash part of the trie id.
-fn child_trie_info(trie_id: &[u8]) -> ChildInfo {
- ChildInfo::new_default(trie_id)
-}
-
-#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]
-pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
-
-impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
-where
- H: Member
- + MaybeSerializeDeserialize
- + Debug
- + AsRef<[u8]>
- + AsMut<[u8]>
- + Copy
- + Default
- + sp_std::hash::Hash
- + Codec,
- Hasher: Hash<Output = H>,
-{
- pub fn new(storage_root: &[u8], code_hash: H) -> Self {
- let mut buf = Vec::new();
- storage_root.using_encoded(|encoded| buf.extend_from_slice(encoded));
- buf.extend_from_slice(code_hash.as_ref());
- RawTombstoneContractInfo(<Hasher as Hash>::hash(&buf[..]), PhantomData)
- }
-}
-
-impl<T: Config> From<AliveContractInfo<T>> for ContractInfo<T> {
- fn from(alive_info: AliveContractInfo<T>) -> Self {
- Self::Alive(alive_info)
- }
-}
-
-/// An error that means that the account requested either doesn't exist or represents a tombstone
-/// account.
-#[cfg_attr(test, derive(PartialEq, Eq, Debug))]
-pub struct ContractAbsentError;
-
-#[derive(Encode, Decode)]
-pub struct DeletedContract {
- pair_count: u32,
- trie_id: TrieId,
-}
-
-pub struct Storage<T>(PhantomData<T>);
-
-impl<T> Storage<T>
-where
- T: Config,
- T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
-{
- /// Reads a storage kv pair of a contract.
- ///
- /// The read is performed from the `trie_id` only. The `address` is not necessary. If the contract
- /// doesn't store under the given `key` `None` is returned.
- pub fn read(trie_id: &TrieId, key: &StorageKey) -> Option<Vec<u8>> {
- child::get_raw(&child_trie_info(&trie_id), &blake2_256(key))
- }
-
- /// Update a storage entry into a contract's kv storage.
- ///
- /// If the `opt_new_value` is `None` then the kv pair is removed.
- ///
- /// This function also updates the bookkeeping info such as: number of total non-empty pairs a
- /// contract owns, the last block the storage was written to, etc. That's why, in contrast to
- /// `read`, this function also requires the `account` ID.
- ///
- /// If the contract specified by the id `account` doesn't exist `Err` is returned.`
- ///
- /// # Panics
- ///
- /// Panics iff the `account` specified is not alive and in storage.
- pub fn write(
- account: &AccountIdOf<T>,
- trie_id: &TrieId,
- key: &StorageKey,
- opt_new_value: Option<Vec<u8>>,
- ) -> DispatchResult {
- let mut new_info = match <ContractInfoOf<T>>::get(account) {
- Some(ContractInfo::Alive(alive)) => alive,
- None | Some(ContractInfo::Tombstone(_)) => panic!("Contract not found"),
- };
-
- let hashed_key = blake2_256(key);
- let child_trie_info = &child_trie_info(&trie_id);
-
- let opt_prev_len = child::len(&child_trie_info, &hashed_key);
-
- // Update the total number of KV pairs and the number of empty pairs.
- match (&opt_prev_len, &opt_new_value) {
- (Some(_), None) => {
- new_info.pair_count = new_info
- .pair_count
- .checked_sub(1)
- .ok_or_else(|| Error::<T>::StorageExhausted)?;
- }
- (None, Some(_)) => {
- new_info.pair_count = new_info
- .pair_count
- .checked_add(1)
- .ok_or_else(|| Error::<T>::StorageExhausted)?;
- }
- (Some(_), Some(_)) => {}
- (None, None) => {}
- }
-
- // Update the total storage size.
- let prev_value_len = opt_prev_len.unwrap_or(0);
- let new_value_len = opt_new_value
- .as_ref()
- .map(|new_value| new_value.len() as u32)
- .unwrap_or(0);
- new_info.storage_size = new_info
- .storage_size
- .checked_sub(prev_value_len)
- .and_then(|val| val.checked_add(new_value_len))
- .ok_or_else(|| Error::<T>::StorageExhausted)?;
-
- new_info.last_write = Some(<frame_system::Pallet<T>>::block_number());
- <ContractInfoOf<T>>::insert(&account, ContractInfo::Alive(new_info));
-
- // Finally, perform the change on the storage.
- match opt_new_value {
- Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, &new_value[..]),
- None => child::kill(&child_trie_info, &hashed_key),
- }
-
- Ok(())
- }
-
- /// Returns the rent allowance set for the contract give by the account id.
- pub fn rent_allowance(account: &AccountIdOf<T>) -> Result<BalanceOf<T>, ContractAbsentError> {
- <ContractInfoOf<T>>::get(account)
- .and_then(|i| i.as_alive().map(|i| i.rent_allowance))
- .ok_or(ContractAbsentError)
- }
-
- /// Set the rent allowance for the contract given by the account id.
- ///
- /// Returns `Err` if the contract doesn't exist or is a tombstone.
- pub fn set_rent_allowance(
- account: &AccountIdOf<T>,
- rent_allowance: BalanceOf<T>,
- ) -> Result<(), ContractAbsentError> {
- <ContractInfoOf<T>>::mutate(account, |maybe_contract_info| match maybe_contract_info {
- Some(ContractInfo::Alive(ref mut alive_info)) => {
- alive_info.rent_allowance = rent_allowance;
- Ok(())
- }
- _ => Err(ContractAbsentError),
- })
- }
-
- /// Creates a new contract descriptor in the storage with the given code hash at the given address.
- ///
- /// Returns `Err` if there is already a contract (or a tombstone) exists at the given address.
- pub fn place_contract(
- account: &AccountIdOf<T>,
- trie_id: TrieId,
- ch: CodeHash<T>,
- ) -> Result<AliveContractInfo<T>, DispatchError> {
- <ContractInfoOf<T>>::try_mutate(account, |existing| {
- if existing.is_some() {
- return Err(Error::<T>::DuplicateContract.into());
- }
-
- let contract = AliveContractInfo::<T> {
- code_hash: ch,
- storage_size: 0,
- trie_id,
- deduct_block:
- // We want to charge rent for the first block in advance. Therefore we
- // treat the contract as if it was created in the last block and then
- // charge rent for it during instantiation.
- <frame_system::Pallet<T>>::block_number().saturating_sub(1u32.into()),
- rent_allowance: <BalanceOf<T>>::max_value(),
- rent_payed: <BalanceOf<T>>::zero(),
- pair_count: 0,
- last_write: None,
- _reserved: None,
- };
-
- *existing = Some(contract.clone().into());
-
- Ok(contract)
- })
- }
-
- /// Push a contract's trie to the deletion queue for lazy removal.
- ///
- /// You must make sure that the contract is also removed or converted into a tombstone
- /// when queuing the trie for deletion.
- pub fn queue_trie_for_deletion(contract: &AliveContractInfo<T>) -> DispatchResult {
- if <DeletionQueue<T>>::decode_len().unwrap_or(0) >= T::DeletionQueueDepth::get() as usize {
- Err(Error::<T>::DeletionQueueFull.into())
- } else {
- <DeletionQueue<T>>::append(DeletedContract {
- pair_count: contract.pair_count,
- trie_id: contract.trie_id.clone(),
- });
- Ok(())
- }
- }
-
- /// Calculates the weight that is necessary to remove one key from the trie and how many
- /// of those keys can be deleted from the deletion queue given the supplied queue length
- /// and weight limit.
- pub fn deletion_budget(queue_len: usize, weight_limit: Weight) -> (u64, u32) {
- let base_weight = T::WeightInfo::on_initialize();
- let weight_per_queue_item = T::WeightInfo::on_initialize_per_queue_item(1)
- - T::WeightInfo::on_initialize_per_queue_item(0);
- let weight_per_key = T::WeightInfo::on_initialize_per_trie_key(1)
- - T::WeightInfo::on_initialize_per_trie_key(0);
- let decoding_weight = weight_per_queue_item.saturating_mul(queue_len as Weight);
-
- // `weight_per_key` being zero makes no sense and would constitute a failure to
- // benchmark properly. We opt for not removing any keys at all in this case.
- let key_budget = weight_limit
- .saturating_sub(base_weight)
- .saturating_sub(decoding_weight)
- .checked_div(weight_per_key)
- .unwrap_or(0) as u32;
-
- (weight_per_key, key_budget)
- }
-
- /// Delete as many items from the deletion queue possible within the supplied weight limit.
- ///
- /// It returns the amount of weight used for that task or `None` when no weight was used
- /// apart from the base weight.
- pub fn process_deletion_queue_batch(weight_limit: Weight) -> Weight {
- let queue_len = <DeletionQueue<T>>::decode_len().unwrap_or(0);
- if queue_len == 0 {
- return weight_limit;
- }
-
- let (weight_per_key, mut remaining_key_budget) =
- Self::deletion_budget(queue_len, weight_limit);
-
- // We want to check whether we have enough weight to decode the queue before
- // proceeding. Too little weight for decoding might happen during runtime upgrades
- // which consume the whole block before the other `on_initialize` blocks are called.
- if remaining_key_budget == 0 {
- return weight_limit;
- }
-
- let mut queue = <DeletionQueue<T>>::get();
-
- while !queue.is_empty() && remaining_key_budget > 0 {
- // Cannot panic due to loop condition
- let trie = &mut queue[0];
- let pair_count = trie.pair_count;
- let outcome =
- child::kill_storage(&child_trie_info(&trie.trie_id), Some(remaining_key_budget));
- if pair_count > remaining_key_budget {
- // Cannot underflow because of the if condition
- trie.pair_count -= remaining_key_budget;
- } else {
- // We do not care to preserve order. The contract is deleted already and
- // noone waits for the trie to be deleted.
- let removed = queue.swap_remove(0);
- match outcome {
- // This should not happen as our budget was large enough to remove all keys.
- KillChildStorageResult::SomeRemaining(_) => {
- log::error!(
- target: "runtime::contracts",
- "After deletion keys are remaining in this child trie: {:?}",
- removed.trie_id,
- );
- }
- KillChildStorageResult::AllRemoved(_) => (),
- }
- }
- remaining_key_budget =
- remaining_key_budget.saturating_sub(remaining_key_budget.min(pair_count));
- }
-
- <DeletionQueue<T>>::put(queue);
- weight_limit.saturating_sub(weight_per_key.saturating_mul(remaining_key_budget as Weight))
- }
-
- /// This generator uses inner counter for account id and applies the hash over `AccountId +
- /// accountid_counter`.
- pub fn generate_trie_id(account_id: &AccountIdOf<T>) -> TrieId {
- // Note that skipping a value due to error is not an issue here.
- // We only need uniqueness, not sequence.
- let new_seed = <AccountCounter<T>>::mutate(|v| {
- *v = v.wrapping_add(1);
- *v
- });
-
- let buf: Vec<_> = account_id
- .as_ref()
- .iter()
- .chain(&new_seed.to_le_bytes())
- .cloned()
- .collect();
- T::Hashing::hash(&buf).as_ref().into()
- }
-
- /// Returns the code hash of the contract specified by `account` ID.
- #[cfg(test)]
- pub fn code_hash(account: &AccountIdOf<T>) -> Result<CodeHash<T>, ContractAbsentError> {
- <ContractInfoOf<T>>::get(account)
- .and_then(|i| i.as_alive().map(|i| i.code_hash))
- .ok_or(ContractAbsentError)
- }
-
- /// Fill up the queue in order to exercise the limits during testing.
- #[cfg(test)]
- pub fn fill_queue_with_dummies() {
- let queue: Vec<_> = (0..T::DeletionQueueDepth::get())
- .map(|_| DeletedContract {
- pair_count: 0,
- trie_id: vec![],
- })
- .collect();
- <DeletionQueue<T>>::put(queue);
- }
-}
pallets/contracts/src/tests.rsdiffbeforeafterboth--- a/pallets/contracts/src/tests.rs
+++ /dev/null
@@ -1,2971 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2018-2021 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.
-
-use crate::{
- BalanceOf, ContractInfo, ContractInfoOf, Pallet, Config, Schedule, Error,
- storage::Storage,
- chain_extension::{
- Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal,
- UncheckedFrom, InitState, ReturnFlags,
- },
- exec::{AccountIdOf, Executable},
- wasm::PrefabWasmModule,
- weights::WeightInfo,
- wasm::ReturnCode as RuntimeReturnCode,
- storage::RawAliveContractInfo,
-};
-use assert_matches::assert_matches;
-use codec::Encode;
-use sp_core::Bytes;
-use sp_runtime::{
- traits::{BlakeTwo256, Hash, IdentityLookup, Convert},
- testing::{Header, H256},
- AccountId32, Perbill,
-};
-use sp_io::hashing::blake2_256;
-use frame_support::{
- assert_ok, assert_err, assert_err_ignore_postinfo, parameter_types, assert_storage_noop,
- traits::{Currency, ReservableCurrency, OnInitialize, GenesisBuild},
- weights::{Weight, PostDispatchInfo, DispatchClass, constants::WEIGHT_PER_SECOND},
- dispatch::DispatchErrorWithPostInfo,
- storage::child,
-};
-use frame_system::{self as system, EventRecord, Phase};
-use pretty_assertions::assert_eq;
-
-use crate as pallet_contracts;
-
-type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
-type Block = frame_system::mocking::MockBlock<Test>;
-
-frame_support::construct_runtime!(
- pub enum Test where
- Block = Block,
- NodeBlock = Block,
- UncheckedExtrinsic = UncheckedExtrinsic,
- {
- System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
- Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
- Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
- Randomness: pallet_randomness_collective_flip::{Pallet, Call, Storage},
- Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},
- }
-);
-
-#[macro_use]
-pub mod test_utils {
- use super::{Test, Balances};
- use crate::{
- ContractInfoOf, CodeHash,
- storage::Storage,
- exec::{StorageKey, AccountIdOf},
- Pallet as Contracts,
- };
- use frame_support::traits::Currency;
-
- pub fn set_storage(addr: &AccountIdOf<Test>, key: &StorageKey, value: Option<Vec<u8>>) {
- let contract_info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- Storage::<Test>::write(addr, &contract_info.trie_id, key, value).unwrap();
- }
- pub fn get_storage(addr: &AccountIdOf<Test>, key: &StorageKey) -> Option<Vec<u8>> {
- let contract_info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- Storage::<Test>::read(&contract_info.trie_id, key)
- }
- pub fn place_contract(address: &AccountIdOf<Test>, code_hash: CodeHash<Test>) {
- let trie_id = Storage::<Test>::generate_trie_id(address);
- set_balance(address, Contracts::<Test>::subsistence_threshold() * 10);
- Storage::<Test>::place_contract(&address, trie_id, code_hash).unwrap();
- }
- pub fn set_balance(who: &AccountIdOf<Test>, amount: u64) {
- let imbalance = Balances::deposit_creating(who, amount);
- drop(imbalance);
- }
- pub fn get_balance(who: &AccountIdOf<Test>) -> u64 {
- Balances::free_balance(who)
- }
- macro_rules! assert_return_code {
- ( $x:expr , $y:expr $(,)? ) => {{
- use sp_std::convert::TryInto;
- assert_eq!(
- u32::from_le_bytes($x.data[..].try_into().unwrap()),
- $y as u32
- );
- }};
- }
- macro_rules! assert_refcount {
- ( $code_hash:expr , $should:expr $(,)? ) => {{
- let is = crate::CodeStorage::<Test>::get($code_hash)
- .map(|m| m.refcount())
- .unwrap_or(0);
- assert_eq!(is, $should);
- }};
- }
-}
-
-thread_local! {
- static TEST_EXTENSION: sp_std::cell::RefCell<TestExtension> = Default::default();
-}
-
-pub struct TestExtension {
- enabled: bool,
- last_seen_buffer: Vec<u8>,
- last_seen_inputs: (u32, u32, u32, u32),
-}
-
-impl TestExtension {
- fn disable() {
- TEST_EXTENSION.with(|e| e.borrow_mut().enabled = false)
- }
-
- fn last_seen_buffer() -> Vec<u8> {
- TEST_EXTENSION.with(|e| e.borrow().last_seen_buffer.clone())
- }
-
- fn last_seen_inputs() -> (u32, u32, u32, u32) {
- TEST_EXTENSION.with(|e| e.borrow().last_seen_inputs.clone())
- }
-}
-
-impl Default for TestExtension {
- fn default() -> Self {
- Self {
- enabled: true,
- last_seen_buffer: vec![],
- last_seen_inputs: (0, 0, 0, 0),
- }
- }
-}
-
-impl ChainExtension<Test> for TestExtension {
- fn call<E>(func_id: u32, env: Environment<E, InitState>) -> ExtensionResult<RetVal>
- where
- E: Ext<T = Test>,
- <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,
- {
- match func_id {
- 0 => {
- let mut env = env.buf_in_buf_out();
- let input = env.read(2)?;
- env.write(&input, false, None)?;
- TEST_EXTENSION.with(|e| e.borrow_mut().last_seen_buffer = input);
- Ok(RetVal::Converging(func_id))
- }
- 1 => {
- let env = env.only_in();
- TEST_EXTENSION.with(|e| {
- e.borrow_mut().last_seen_inputs =
- (env.val0(), env.val1(), env.val2(), env.val3())
- });
- Ok(RetVal::Converging(func_id))
- }
- 2 => {
- let mut env = env.buf_in_buf_out();
- let weight = env.read(2)?[1].into();
- env.charge_weight(weight)?;
- Ok(RetVal::Converging(func_id))
- }
- 3 => Ok(RetVal::Diverging {
- flags: ReturnFlags::REVERT,
- data: vec![42, 99],
- }),
- _ => {
- panic!(
- "Passed unknown func_id to test chain extension: {}",
- func_id
- );
- }
- }
- }
-
- fn enabled() -> bool {
- TEST_EXTENSION.with(|e| e.borrow().enabled)
- }
-}
-
-parameter_types! {
- pub const BlockHashCount: u64 = 250;
- pub BlockWeights: frame_system::limits::BlockWeights =
- frame_system::limits::BlockWeights::simple_max(2 * WEIGHT_PER_SECOND);
- pub static ExistentialDeposit: u64 = 0;
-}
-impl frame_system::Config for Test {
- type BaseCallFilter = ();
- type BlockWeights = BlockWeights;
- type BlockLength = ();
- type DbWeight = ();
- type Origin = Origin;
- type Index = u64;
- type BlockNumber = u64;
- type Hash = H256;
- type Call = Call;
- type Hashing = BlakeTwo256;
- type AccountId = AccountId32;
- type Lookup = IdentityLookup<Self::AccountId>;
- type Header = Header;
- type Event = Event;
- type BlockHashCount = BlockHashCount;
- type Version = ();
- type PalletInfo = PalletInfo;
- type AccountData = pallet_balances::AccountData<u64>;
- type OnNewAccount = ();
- type OnKilledAccount = ();
- type SystemWeightInfo = ();
- type SS58Prefix = ();
- type OnSetCode = ();
-}
-impl pallet_balances::Config for Test {
- type MaxLocks = ();
- type Balance = u64;
- type Event = Event;
- type DustRemoval = ();
- type ExistentialDeposit = ExistentialDeposit;
- type AccountStore = System;
- type WeightInfo = ();
-}
-parameter_types! {
- pub const MinimumPeriod: u64 = 1;
-}
-impl pallet_timestamp::Config for Test {
- type Moment = u64;
- type OnTimestampSet = ();
- type MinimumPeriod = MinimumPeriod;
- type WeightInfo = ();
-}
-parameter_types! {
- pub const SignedClaimHandicap: u64 = 2;
- pub const TombstoneDeposit: u64 = 16;
- pub const DepositPerContract: u64 = 8 * DepositPerStorageByte::get();
- pub const DepositPerStorageByte: u64 = 10_000;
- pub const DepositPerStorageItem: u64 = 10_000;
- pub RentFraction: Perbill = Perbill::from_rational(4u32, 10_000u32);
- pub const SurchargeReward: u64 = 500_000;
- pub const MaxDepth: u32 = 100;
- pub const MaxValueSize: u32 = 16_384;
- pub const DeletionQueueDepth: u32 = 1024;
- pub const DeletionWeightLimit: Weight = 500_000_000_000;
- pub const MaxCodeSize: u32 = 2 * 1024;
-}
-
-parameter_types! {
- pub const TransactionByteFee: u64 = 0;
-}
-
-impl Convert<Weight, BalanceOf<Self>> for Test {
- fn convert(w: Weight) -> BalanceOf<Self> {
- w
- }
-}
-
-impl Config for Test {
- type Time = Timestamp;
- type Randomness = Randomness;
- type Currency = Balances;
- type Event = Event;
- type RentPayment = ();
- type SignedClaimHandicap = SignedClaimHandicap;
- type TombstoneDeposit = TombstoneDeposit;
- type DepositPerContract = DepositPerContract;
- type DepositPerStorageByte = DepositPerStorageByte;
- type DepositPerStorageItem = DepositPerStorageItem;
- type RentFraction = RentFraction;
- type SurchargeReward = SurchargeReward;
- type MaxDepth = MaxDepth;
- type MaxValueSize = MaxValueSize;
- type WeightPrice = Self;
- type WeightInfo = ();
- type ChainExtension = TestExtension;
- type DeletionQueueDepth = DeletionQueueDepth;
- type DeletionWeightLimit = DeletionWeightLimit;
- type MaxCodeSize = MaxCodeSize;
-}
-
-pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
-pub const BOB: AccountId32 = AccountId32::new([2u8; 32]);
-pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]);
-pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]);
-
-const GAS_LIMIT: Weight = 10_000_000_000;
-
-pub struct ExtBuilder {
- existential_deposit: u64,
-}
-impl Default for ExtBuilder {
- fn default() -> Self {
- Self {
- existential_deposit: 1,
- }
- }
-}
-impl ExtBuilder {
- pub fn existential_deposit(mut self, existential_deposit: u64) -> Self {
- self.existential_deposit = existential_deposit;
- self
- }
- pub fn set_associated_consts(&self) {
- EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
- }
- pub fn build(self) -> sp_io::TestExternalities {
- self.set_associated_consts();
- let mut t = frame_system::GenesisConfig::default()
- .build_storage::<Test>()
- .unwrap();
- pallet_balances::GenesisConfig::<Test> { balances: vec![] }
- .assimilate_storage(&mut t)
- .unwrap();
- pallet_contracts::GenesisConfig {
- current_schedule: Schedule::<Test> {
- enable_println: true,
- ..Default::default()
- },
- }
- .assimilate_storage(&mut t)
- .unwrap();
- let mut ext = sp_io::TestExternalities::new(t);
- ext.execute_with(|| System::set_block_number(1));
- ext
- }
-}
-
-/// Load a given wasm module represented by a .wat file and returns a wasm binary contents along
-/// with it's hash.
-///
-/// The fixture files are located under the `fixtures/` directory.
-fn compile_module<T>(fixture_name: &str) -> wat::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>
-where
- T: frame_system::Config,
-{
- let fixture_path = ["fixtures/", fixture_name, ".wat"].concat();
- let wasm_binary = wat::parse_file(fixture_path)?;
- let code_hash = T::Hashing::hash(&wasm_binary);
- Ok((wasm_binary, code_hash))
-}
-
-// Perform a call to a plain account.
-// The actual transfer fails because we can only call contracts.
-// Then we check that at least the base costs where charged (no runtime gas costs.)
-#[test]
-fn calling_plain_account_fails() {
- ExtBuilder::default().build().execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 100_000_000);
- let base_cost = <<Test as Config>::WeightInfo as WeightInfo>::call(0);
-
- assert_eq!(
- Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, Vec::new()),
- Err(DispatchErrorWithPostInfo {
- error: Error::<Test>::NotCallable.into(),
- post_info: PostDispatchInfo {
- actual_weight: Some(base_cost),
- pays_fee: Default::default(),
- },
- })
- );
- });
-}
-
-#[test]
-fn account_removal_does_not_remove_storage() {
- use self::test_utils::{set_storage, get_storage};
-
- ExtBuilder::default()
- .existential_deposit(100)
- .build()
- .execute_with(|| {
- let trie_id1 = Storage::<Test>::generate_trie_id(&ALICE);
- let trie_id2 = Storage::<Test>::generate_trie_id(&BOB);
- let key1 = &[1; 32];
- let key2 = &[2; 32];
-
- // Set up two accounts with free balance above the existential threshold.
- {
- let alice_contract_info = ContractInfo::Alive(RawAliveContractInfo {
- trie_id: trie_id1.clone(),
- storage_size: 0,
- pair_count: 0,
- deduct_block: System::block_number(),
- code_hash: H256::repeat_byte(1),
- rent_allowance: 40,
- rent_payed: 0,
- last_write: None,
- _reserved: None,
- });
- let _ = Balances::deposit_creating(&ALICE, 110);
- ContractInfoOf::<Test>::insert(ALICE, &alice_contract_info);
- set_storage(&ALICE, &key1, Some(b"1".to_vec()));
- set_storage(&ALICE, &key2, Some(b"2".to_vec()));
-
- let bob_contract_info = ContractInfo::Alive(RawAliveContractInfo {
- trie_id: trie_id2.clone(),
- storage_size: 0,
- pair_count: 0,
- deduct_block: System::block_number(),
- code_hash: H256::repeat_byte(2),
- rent_allowance: 40,
- rent_payed: 0,
- last_write: None,
- _reserved: None,
- });
- let _ = Balances::deposit_creating(&BOB, 110);
- ContractInfoOf::<Test>::insert(BOB, &bob_contract_info);
- set_storage(&BOB, &key1, Some(b"3".to_vec()));
- set_storage(&BOB, &key2, Some(b"4".to_vec()));
- }
-
- // Transfer funds from ALICE account of such amount that after this transfer
- // the balance of the ALICE account will be below the existential threshold.
- //
- // This does not remove the contract storage as we are not notified about a
- // account removal. This cannot happen in reality because a contract can only
- // remove itself by `seal_terminate`. There is no external event that can remove
- // the account appart from that.
- assert_ok!(Balances::transfer(Origin::signed(ALICE), BOB, 20));
-
- // Verify that no entries are removed.
- {
- assert_eq!(get_storage(&ALICE, key1), Some(b"1".to_vec()));
- assert_eq!(get_storage(&ALICE, key2), Some(b"2".to_vec()));
-
- assert_eq!(get_storage(&BOB, key1), Some(b"3".to_vec()));
- assert_eq!(get_storage(&BOB, key2), Some(b"4".to_vec()));
- }
- });
-}
-
-#[test]
-fn instantiate_and_call_and_deposit_event() {
- let (wasm, code_hash) = compile_module::<Test>("return_from_start_fn").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(100)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- let subsistence = Pallet::<Test>::subsistence_threshold();
-
- // Check at the end to get hash on error easily
- let creation = Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- );
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- assert_eq!(
- System::events(),
- vec![
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(ALICE.clone())),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- ALICE, 1_000_000
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(addr.clone())),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- addr.clone(),
- subsistence * 100
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Transfer(
- ALICE,
- addr.clone(),
- subsistence * 100
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::CodeStored(code_hash.into())),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::ContractEmitted(
- addr.clone(),
- vec![1, 2, 3, 4]
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Instantiated(
- ALICE,
- addr.clone()
- )),
- topics: vec![],
- },
- ]
- );
-
- assert_ok!(creation);
- assert!(ContractInfoOf::<Test>::contains_key(&addr));
- });
-}
-
-#[test]
-fn deposit_event_max_value_limit() {
- let (wasm, code_hash) = compile_module::<Test>("event_size").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Call contract with allowed storage value.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT * 2, // we are copying a huge buffer,
- <Test as Config>::MaxValueSize::get().encode(),
- ));
-
- // Call contract with too large a storage value.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr,
- 0,
- GAS_LIMIT,
- (<Test as Config>::MaxValueSize::get() + 1).encode(),
- ),
- Error::<Test>::ValueTooLarge,
- );
- });
-}
-
-#[test]
-fn run_out_of_gas() {
- let (wasm, code_hash) = compile_module::<Test>("run_out_of_gas").unwrap();
- let subsistence = Pallet::<Test>::subsistence_threshold();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100 * subsistence,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Call the contract with a fixed gas limit. It must run out of gas because it just
- // loops forever.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr, // newly created account
- 0,
- 67_500_000,
- vec![],
- ),
- Error::<Test>::OutOfGas,
- );
- });
-}
-
-/// Input data for each call in set_rent code
-mod call {
- use super::{AccountIdOf, Test};
- pub fn set_storage_4_byte() -> Vec<u8> {
- 0u32.to_le_bytes().to_vec()
- }
- pub fn remove_storage_4_byte() -> Vec<u8> {
- 1u32.to_le_bytes().to_vec()
- }
- #[allow(dead_code)]
- pub fn transfer(to: &AccountIdOf<Test>) -> Vec<u8> {
- 2u32.to_le_bytes()
- .iter()
- .chain(AsRef::<[u8]>::as_ref(to))
- .cloned()
- .collect()
- }
- pub fn null() -> Vec<u8> {
- 3u32.to_le_bytes().to_vec()
- }
-}
-
-#[test]
-fn storage_size() {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
-
- // Storage size
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- // rent_allowance
- <Test as pallet_balances::Config>::Balance::from(10_000u32).encode(),
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let bob_contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(bob_contract.storage_size, 4);
- assert_eq!(bob_contract.pair_count, 1,);
-
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::set_storage_4_byte()
- ));
- let bob_contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(bob_contract.storage_size, 4 + 4);
- assert_eq!(bob_contract.pair_count, 2,);
-
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::remove_storage_4_byte()
- ));
- let bob_contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(bob_contract.storage_size, 4);
- assert_eq!(bob_contract.pair_count, 1,);
- });
-}
-
-#[test]
-fn empty_kv_pairs() {
- let (wasm, code_hash) = compile_module::<Test>("set_empty_storage").unwrap();
-
- ExtBuilder::default().build().execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let bob_contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
-
- assert_eq!(bob_contract.storage_size, 0,);
- assert_eq!(bob_contract.pair_count, 1,);
- });
-}
-
-fn initialize_block(number: u64) {
- System::initialize(
- &number,
- &[0u8; 32].into(),
- &Default::default(),
- Default::default(),
- );
-}
-
-#[test]
-fn deduct_blocks() {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
- let endowment: BalanceOf<Test> = 100_000;
- let allowance: BalanceOf<Test> = 70_000;
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- endowment,
- GAS_LIMIT,
- wasm,
- allowance.encode(),
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let code_len: BalanceOf<Test> =
- PrefabWasmModule::<Test>::from_storage_noinstr(contract.code_hash)
- .unwrap()
- .occupied_storage()
- .into();
-
- // The instantiation deducted the rent for one block immediately
- let rent0 = <Test as Config>::RentFraction::get()
- // (base_deposit(8) + bytes in storage(4) + size of code) * byte_price
- // + 1 storage item (10_000) - free_balance
- .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - endowment)
- // blocks to rent
- * 1;
- assert!(rent0 > 0);
- assert_eq!(contract.rent_allowance, allowance - rent0);
- assert_eq!(contract.deduct_block, 1);
- assert_eq!(Balances::free_balance(&addr), endowment - rent0);
-
- // Advance 4 blocks
- initialize_block(5);
-
- // Trigger rent through call
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ));
-
- // Check result
- let rent = <Test as Config>::RentFraction::get()
- .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0))
- * 4;
- let contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(contract.rent_allowance, allowance - rent0 - rent);
- assert_eq!(contract.deduct_block, 5);
- assert_eq!(Balances::free_balance(&addr), endowment - rent0 - rent);
-
- // Advance 2 blocks more
- initialize_block(7);
-
- // Trigger rent through call
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ));
-
- // Check result
- let rent_2 = <Test as Config>::RentFraction::get()
- .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0 - rent))
- * 2;
- let contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2);
- assert_eq!(contract.deduct_block, 7);
- assert_eq!(
- Balances::free_balance(&addr),
- endowment - rent0 - rent - rent_2
- );
-
- // Second call on same block should have no effect on rent
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ));
- let contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2);
- assert_eq!(contract.deduct_block, 7);
- assert_eq!(
- Balances::free_balance(&addr),
- endowment - rent0 - rent - rent_2
- )
- });
-}
-
-#[test]
-fn inherent_claim_surcharge_contract_removals() {
- removals(|addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok());
-}
-
-#[test]
-fn signed_claim_surcharge_contract_removals() {
- removals(|addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok());
-}
-
-#[test]
-fn claim_surcharge_malus() {
- // Test surcharge malus for inherent
- claim_surcharge(
- 8,
- |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),
- true,
- );
- claim_surcharge(
- 7,
- |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),
- true,
- );
- claim_surcharge(
- 6,
- |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),
- true,
- );
- claim_surcharge(
- 5,
- |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),
- false,
- );
-
- // Test surcharge malus for signed
- claim_surcharge(
- 8,
- |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),
- true,
- );
- claim_surcharge(
- 7,
- |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),
- false,
- );
- claim_surcharge(
- 6,
- |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),
- false,
- );
- claim_surcharge(
- 5,
- |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),
- false,
- );
-}
-
-/// Claim surcharge with the given trigger_call at the given blocks.
-/// If `removes` is true then assert that the contract is a tombstone.
-fn claim_surcharge(blocks: u64, trigger_call: impl Fn(AccountIdOf<Test>) -> bool, removes: bool) {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- <Test as pallet_balances::Config>::Balance::from(30_000u32).encode(), // rent allowance
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Advance blocks
- initialize_block(blocks);
-
- // Trigger rent through call
- assert_eq!(trigger_call(addr.clone()), removes);
-
- if removes {
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- } else {
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .is_some());
- }
- });
-}
-
-/// Test for all kind of removals for the given trigger:
-/// * if balance is reached and balance > subsistence threshold
-/// * if allowance is exceeded
-/// * if balance is reached and balance < subsistence threshold
-/// * this case cannot be triggered by a contract: we check whether a tombstone is left
-fn removals(trigger_call: impl Fn(AccountIdOf<Test>) -> bool) {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
-
- // Balance reached and superior to subsistence threshold
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 70_000,
- GAS_LIMIT,
- wasm.clone(),
- <Test as pallet_balances::Config>::Balance::from(100_000u32).encode(), // rent allowance
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let allowance = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance;
- let balance = Balances::free_balance(&addr);
-
- let subsistence_threshold = Pallet::<Test>::subsistence_threshold();
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert_eq!(
- ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance,
- allowance,
- );
- assert_eq!(Balances::free_balance(&addr), balance);
-
- // Advance blocks
- initialize_block(27);
-
- // Trigger rent through call (should remove the contract)
- assert!(trigger_call(addr.clone()));
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- assert_eq!(Balances::free_balance(&addr), subsistence_threshold);
-
- // Advance blocks
- initialize_block(30);
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- assert_eq!(Balances::free_balance(&addr), subsistence_threshold);
- });
-
- // Allowance exceeded
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm.clone(),
- <Test as pallet_balances::Config>::Balance::from(70_000u32).encode(), // rent allowance
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let allowance = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance;
- let balance = Balances::free_balance(&addr);
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert_eq!(
- ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance,
- allowance,
- );
- assert_eq!(Balances::free_balance(&addr), balance);
-
- // Advance blocks
- initialize_block(27);
-
- // Trigger rent through call
- assert!(trigger_call(addr.clone()));
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- // Balance should be initial balance - initial rent_allowance
- assert_eq!(Balances::free_balance(&addr), 30_000);
-
- // Advance blocks
- initialize_block(20);
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- assert_eq!(Balances::free_balance(&addr), 30_000);
- });
-
- // Balance reached and inferior to subsistence threshold
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let subsistence_threshold = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, subsistence_threshold * 1000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence_threshold * 100,
- GAS_LIMIT,
- wasm,
- (subsistence_threshold * 100).encode(), // rent allowance
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let allowance = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance;
- let balance = Balances::free_balance(&addr);
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert_eq!(
- ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap()
- .rent_allowance,
- allowance,
- );
- assert_eq!(Balances::free_balance(&addr), balance,);
-
- // Make contract have exactly the subsistence threshold
- Balances::make_free_balance_be(&addr, subsistence_threshold);
- assert_eq!(Balances::free_balance(&addr), subsistence_threshold);
-
- // Advance blocks (should remove as balance is exactly subsistence)
- initialize_block(10);
-
- // Trigger rent through call
- assert!(trigger_call(addr.clone()));
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Tombstone(_))
- );
- assert_eq!(Balances::free_balance(&addr), subsistence_threshold);
-
- // Advance blocks
- initialize_block(20);
-
- // Trigger rent must have no effect
- assert!(!trigger_call(addr.clone()));
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Tombstone(_))
- );
- assert_eq!(Balances::free_balance(&addr), subsistence_threshold);
- });
-}
-
-#[test]
-fn call_removed_contract() {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
-
- // Balance reached and superior to subsistence threshold
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- // rent allowance
- <Test as pallet_balances::Config>::Balance::from(10_000u32).encode(),
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Calling contract should succeed.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ));
-
- // Advance blocks
- initialize_block(27);
-
- // Calling contract should deny access because rent cannot be paid.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ),
- Error::<Test>::NotCallable
- );
- // No event is generated because the contract is not actually removed.
- assert_eq!(System::events(), vec![]);
-
- // Subsequent contract calls should also fail.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ),
- Error::<Test>::NotCallable
- );
-
- // A snitch can now remove the contract
- assert_ok!(Contracts::claim_surcharge(
- Origin::none(),
- addr.clone(),
- Some(ALICE)
- ));
- assert!(ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_tombstone()
- .is_some());
- })
-}
-
-#[test]
-fn default_rent_allowance_on_instantiate() {
- let (wasm, code_hash) = compile_module::<Test>("check_default_rent_allowance").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let contract = ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let code_len: BalanceOf<Test> =
- PrefabWasmModule::<Test>::from_storage_noinstr(contract.code_hash)
- .unwrap()
- .occupied_storage()
- .into();
-
- // The instantiation deducted the rent for one block immediately
- let first_rent = <Test as Config>::RentFraction::get()
- // (base_deposit(8) + code_len) * byte_price - free_balance
- .mul_ceil((8 + code_len) * 10_000 - 30_000)
- // blocks to rent
- * 1;
- assert_eq!(
- contract.rent_allowance,
- <BalanceOf<Test>>::max_value() - first_rent
- );
-
- // Advance blocks
- initialize_block(5);
-
- // Trigger rent through call
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ));
-
- // Check contract is still alive
- let contract = ContractInfoOf::<Test>::get(&addr).unwrap().get_alive();
- assert!(contract.is_some())
- });
-}
-
-#[test]
-fn restorations_dirty_storage_and_different_storage() {
- restoration(true, true, false);
-}
-
-#[test]
-fn restorations_dirty_storage() {
- restoration(false, true, false);
-}
-
-#[test]
-fn restoration_different_storage() {
- restoration(true, false, false);
-}
-
-#[test]
-fn restoration_code_evicted() {
- restoration(false, false, true);
-}
-
-#[test]
-fn restoration_success() {
- restoration(false, false, false);
-}
-
-fn restoration(
- test_different_storage: bool,
- test_restore_to_with_dirty_storage: bool,
- test_code_evicted: bool,
-) {
- let (set_rent_wasm, set_rent_code_hash) = compile_module::<Test>("set_rent").unwrap();
- let (restoration_wasm, restoration_code_hash) = compile_module::<Test>("restoration").unwrap();
- let allowance: <Test as pallet_balances::Config>::Balance = 10_000;
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- // Create an account with address `BOB` with code `CODE_SET_RENT`.
- // The input parameter sets the rent allowance to 0.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- set_rent_wasm.clone(),
- allowance.encode(),
- vec![],
- ));
- let addr_bob = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[]);
-
- let mut events = vec![
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(ALICE)),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- ALICE, 1_000_000,
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(addr_bob.clone())),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- addr_bob.clone(),
- 30_000,
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Transfer(
- ALICE,
- addr_bob.clone(),
- 30_000,
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::CodeStored(
- set_rent_code_hash.into(),
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Instantiated(
- ALICE,
- addr_bob.clone(),
- )),
- topics: vec![],
- },
- ];
-
- // Create another contract from the same code in order to increment the codes
- // refcounter so that it stays on chain.
- if !test_code_evicted {
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 20_000,
- GAS_LIMIT,
- set_rent_wasm,
- allowance.encode(),
- vec![1],
- ));
- assert_refcount!(set_rent_code_hash, 2);
- let addr_dummy = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[1]);
- events.extend(
- [
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(
- addr_dummy.clone(),
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- addr_dummy.clone(),
- 20_000,
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Transfer(
- ALICE,
- addr_dummy.clone(),
- 20_000,
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Instantiated(
- ALICE,
- addr_dummy.clone(),
- )),
- topics: vec![],
- },
- ]
- .iter()
- .cloned(),
- );
- }
-
- assert_eq!(System::events(), events);
-
- // Check if `BOB` was created successfully and that the rent allowance is below what
- // we specified as the first rent was already collected.
- let bob_contract = ContractInfoOf::<Test>::get(&addr_bob)
- .unwrap()
- .get_alive()
- .unwrap();
- assert!(bob_contract.rent_allowance < allowance);
-
- if test_different_storage {
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- call::set_storage_4_byte()
- ));
- }
-
- // Advance blocks in order to make the contract run out of money for rent.
- initialize_block(27);
-
- // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 20_000
- // we expect that it is no longer callable but keeps existing until someone
- // calls `claim_surcharge`.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- call::null()
- ),
- Error::<Test>::NotCallable
- );
- assert!(System::events().is_empty());
- assert!(ContractInfoOf::<Test>::get(&addr_bob)
- .unwrap()
- .get_alive()
- .is_some());
- assert_ok!(Contracts::claim_surcharge(
- Origin::none(),
- addr_bob.clone(),
- Some(ALICE)
- ));
- assert!(ContractInfoOf::<Test>::get(&addr_bob)
- .unwrap()
- .get_tombstone()
- .is_some());
- if test_code_evicted {
- assert_refcount!(set_rent_code_hash, 0);
- } else {
- assert_refcount!(set_rent_code_hash, 1);
- }
-
- // Create another account with the address `DJANGO` with `CODE_RESTORATION`.
- //
- // Note that we can't use `ALICE` for creating `DJANGO` so we create yet another
- // account `CHARLIE` and create `DJANGO` with it.
- let _ = Balances::deposit_creating(&CHARLIE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(CHARLIE),
- 30_000,
- GAS_LIMIT,
- restoration_wasm,
- vec![],
- vec![],
- ));
- let addr_django = Contracts::contract_address(&CHARLIE, &restoration_code_hash, &[]);
-
- // Before performing a call to `DJANGO` save its original trie id.
- let django_trie_id = ContractInfoOf::<Test>::get(&addr_django)
- .unwrap()
- .get_alive()
- .unwrap()
- .trie_id;
-
- // The trie is regarded as 'dirty' when it was written to in the current block.
- if !test_restore_to_with_dirty_storage {
- // Advance 1 block.
- initialize_block(28);
- }
-
- // Perform a call to `DJANGO`. This should either perform restoration successfully or
- // fail depending on the test parameters.
- let perform_the_restoration = || {
- Contracts::call(
- Origin::signed(ALICE),
- addr_django.clone(),
- 0,
- GAS_LIMIT,
- set_rent_code_hash
- .as_ref()
- .iter()
- .chain(AsRef::<[u8]>::as_ref(&addr_bob))
- .cloned()
- .collect(),
- )
- };
-
- // The key that is used in the restorer contract but is not in the target contract.
- // Is supplied as delta to the restoration. We need it to check whether the key
- // is properly removed on success but still there on failure.
- let delta_key = {
- let mut key = [0u8; 32];
- key[0] = 1;
- key
- };
-
- if test_different_storage || test_restore_to_with_dirty_storage || test_code_evicted {
- // Parametrization of the test imply restoration failure. Check that `DJANGO` aka
- // restoration contract is still in place and also that `BOB` doesn't exist.
- let result = perform_the_restoration();
- assert!(ContractInfoOf::<Test>::get(&addr_bob)
- .unwrap()
- .get_tombstone()
- .is_some());
- let django_contract = ContractInfoOf::<Test>::get(&addr_django)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(django_contract.storage_size, 8);
- assert_eq!(django_contract.trie_id, django_trie_id);
- assert_eq!(django_contract.deduct_block, System::block_number());
- assert_eq!(
- Storage::<Test>::read(&django_trie_id, &delta_key),
- Some(vec![40, 0, 0, 0]),
- );
- match (
- test_different_storage,
- test_restore_to_with_dirty_storage,
- test_code_evicted,
- ) {
- (true, false, false) => {
- assert_err_ignore_postinfo!(result, Error::<Test>::InvalidTombstone,);
- assert_eq!(System::events(), vec![]);
- }
- (_, true, false) => {
- assert_err_ignore_postinfo!(result, Error::<Test>::InvalidContractOrigin,);
- assert_eq!(
- System::events(),
- vec![
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Evicted(addr_bob)),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(
- CHARLIE
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- CHARLIE, 1_000_000
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::NewAccount(
- addr_django.clone()
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Endowed(
- addr_django.clone(),
- 30_000
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(
- pallet_balances::Event::Transfer(
- CHARLIE,
- addr_django.clone(),
- 30_000
- )
- ),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::CodeStored(
- restoration_code_hash
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Instantiated(
- CHARLIE,
- addr_django.clone()
- )),
- topics: vec![],
- },
- ]
- );
- }
- (false, false, true) => {
- assert_err_ignore_postinfo!(result, Error::<Test>::CodeNotFound,);
- assert_refcount!(set_rent_code_hash, 0);
- assert_eq!(System::events(), vec![]);
- }
- _ => unreachable!(),
- }
- } else {
- assert_ok!(perform_the_restoration());
- assert_refcount!(set_rent_code_hash, 2);
-
- // Here we expect that the restoration is succeeded. Check that the restoration
- // contract `DJANGO` ceased to exist and that `BOB` returned back.
- let bob_contract = ContractInfoOf::<Test>::get(&addr_bob)
- .unwrap()
- .get_alive()
- .unwrap();
- assert_eq!(bob_contract.rent_allowance, 50);
- assert_eq!(bob_contract.storage_size, 4);
- assert_eq!(bob_contract.trie_id, django_trie_id);
- assert_eq!(bob_contract.deduct_block, System::block_number());
- assert!(ContractInfoOf::<Test>::get(&addr_django).is_none());
- assert_matches!(Storage::<Test>::read(&django_trie_id, &delta_key), None);
- assert_eq!(
- System::events(),
- vec![
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::CodeRemoved(
- restoration_code_hash
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(system::Event::KilledAccount(
- addr_django.clone()
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Restored(
- addr_django,
- addr_bob,
- bob_contract.code_hash,
- 50
- )),
- topics: vec![],
- },
- ]
- );
- }
- });
-}
-
-#[test]
-fn storage_max_value_limit() {
- let (wasm, code_hash) = compile_module::<Test>("storage_size").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- ContractInfoOf::<Test>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
-
- // Call contract with allowed storage value.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT * 2, // we are copying a huge buffer
- <Test as Config>::MaxValueSize::get().encode(),
- ));
-
- // Call contract with too large a storage value.
- assert_err_ignore_postinfo!(
- Contracts::call(
- Origin::signed(ALICE),
- addr,
- 0,
- GAS_LIMIT,
- (<Test as Config>::MaxValueSize::get() + 1).encode(),
- ),
- Error::<Test>::ValueTooLarge,
- );
- });
-}
-
-#[test]
-fn deploy_and_call_other_contract() {
- let (callee_wasm, callee_code_hash) = compile_module::<Test>("return_with_data").unwrap();
- let (caller_wasm, caller_code_hash) = compile_module::<Test>("caller_contract").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- caller_wasm,
- vec![],
- vec![],
- ));
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- callee_wasm,
- 0u32.to_le_bytes().encode(),
- vec![42],
- ));
-
- // Call BOB contract, which attempts to instantiate and call the callee contract and
- // makes various assertions on the results from those calls.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- Contracts::contract_address(&ALICE, &caller_code_hash, &[]),
- 0,
- GAS_LIMIT,
- callee_code_hash.as_ref().to_vec(),
- ));
- });
-}
-
-#[test]
-fn cannot_self_destruct_through_draning() {
- let (wasm, code_hash) = compile_module::<Test>("drain").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- // Instantiate the BOB contract.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Check that the BOB contract has been instantiated.
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Alive(_))
- );
-
- // Call BOB which makes it send all funds to the zero address
- // The contract code asserts that the correct error value is returned.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr,
- 0,
- GAS_LIMIT,
- vec![],
- ));
- });
-}
-
-#[test]
-fn cannot_self_destruct_while_live() {
- let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- // Instantiate the BOB contract.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Check that the BOB contract has been instantiated.
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Alive(_))
- );
-
- // Call BOB with input data, forcing it make a recursive call to itself to
- // self-destruct, resulting in a trap.
- assert_err_ignore_postinfo!(
- Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![0],),
- Error::<Test>::ContractTrapped,
- );
-
- // Check that BOB is still alive.
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Alive(_))
- );
- });
-}
-
-#[test]
-fn self_destruct_works() {
- let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- let _ = Balances::deposit_creating(&DJANGO, 1_000_000);
-
- // Instantiate the BOB contract.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Check that the BOB contract has been instantiated.
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr),
- Some(ContractInfo::Alive(_))
- );
-
- // Drop all previous events
- initialize_block(2);
-
- // Call BOB without input data which triggers termination.
- assert_matches!(
- Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),
- Ok(_)
- );
-
- pretty_assertions::assert_eq!(
- System::events(),
- vec![
- EventRecord {
- phase: Phase::Initialization,
- event: Event::frame_system(frame_system::Event::KilledAccount(
- addr.clone()
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_balances(pallet_balances::Event::Transfer(
- addr.clone(),
- DJANGO,
- 93_086
- )),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::CodeRemoved(code_hash)),
- topics: vec![],
- },
- EventRecord {
- phase: Phase::Initialization,
- event: Event::pallet_contracts(crate::Event::Terminated(
- addr.clone(),
- DJANGO
- )),
- topics: vec![],
- },
- ]
- );
-
- // Check that account is gone
- assert!(ContractInfoOf::<Test>::get(&addr).is_none());
-
- // check that the beneficiary (django) got remaining balance
- // some rent was deducted before termination
- assert_eq!(Balances::free_balance(DJANGO), 1_093_086);
- });
-}
-
-// This tests that one contract cannot prevent another from self-destructing by sending it
-// additional funds after it has been drained.
-#[test]
-fn destroy_contract_and_transfer_funds() {
- let (callee_wasm, callee_code_hash) = compile_module::<Test>("self_destruct").unwrap();
- let (caller_wasm, caller_code_hash) = compile_module::<Test>("destroy_and_transfer").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 200_000,
- GAS_LIMIT,
- callee_wasm,
- vec![],
- vec![42]
- ));
-
- // This deploys the BOB contract, which in turn deploys the CHARLIE contract during
- // construction.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 200_000,
- GAS_LIMIT,
- caller_wasm,
- callee_code_hash.as_ref().to_vec(),
- vec![],
- ));
- let addr_bob = Contracts::contract_address(&ALICE, &caller_code_hash, &[]);
- let addr_charlie =
- Contracts::contract_address(&addr_bob, &callee_code_hash, &[0x47, 0x11]);
-
- // Check that the CHARLIE contract has been instantiated.
- assert_matches!(
- ContractInfoOf::<Test>::get(&addr_charlie),
- Some(ContractInfo::Alive(_))
- );
-
- // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr_bob,
- 0,
- GAS_LIMIT,
- addr_charlie.encode(),
- ));
-
- // Check that CHARLIE has moved on to the great beyond (ie. died).
- assert!(ContractInfoOf::<Test>::get(&addr_charlie).is_none());
- });
-}
-
-#[test]
-fn cannot_self_destruct_in_constructor() {
- let (wasm, _) = compile_module::<Test>("self_destructing_constructor").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- // Fail to instantiate the BOB because the contructor calls seal_terminate.
- assert_err_ignore_postinfo!(
- Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ),
- Error::<Test>::NotCallable,
- );
- });
-}
-
-#[test]
-fn crypto_hashes() {
- let (wasm, code_hash) = compile_module::<Test>("crypto_hashes").unwrap();
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
-
- // Instantiate the CRYPTO_HASHES contract.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 100_000,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- // Perform the call.
- let input = b"_DEAD_BEEF";
- use sp_io::hashing::*;
- // Wraps a hash function into a more dynamic form usable for testing.
- macro_rules! dyn_hash_fn {
- ($name:ident) => {
- Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice())
- };
- }
- // All hash functions and their associated output byte lengths.
- let test_cases: &[(Box<dyn Fn(&[u8]) -> Box<[u8]>>, usize)] = &[
- (dyn_hash_fn!(sha2_256), 32),
- (dyn_hash_fn!(keccak_256), 32),
- (dyn_hash_fn!(blake2_256), 32),
- (dyn_hash_fn!(blake2_128), 16),
- ];
- // Test the given hash functions for the input: "_DEAD_BEEF"
- for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() {
- // We offset data in the contract tables by 1.
- let mut params = vec![(n + 1) as u8];
- params.extend_from_slice(input);
- let result = <Pallet<Test>>::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, params)
- .result
- .unwrap();
- assert!(result.is_success());
- let expected = hash_fn(input.as_ref());
- assert_eq!(&result.data[..*expected_size], &*expected);
- }
- })
-}
-
-#[test]
-fn transfer_return_code() {
- let (wasm, code_hash) = compile_module::<Test>("transfer_return_code").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- wasm,
- vec![],
- vec![],
- ),);
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Contract has only the minimal balance so any transfer will return BelowSubsistence.
- Balances::make_free_balance_be(&addr, subsistence);
- let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![])
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
-
- // Contract has enough total balance in order to not go below the subsistence
- // threshold when transfering 100 balance but this balance is reserved so
- // the transfer still fails but with another return code.
- Balances::make_free_balance_be(&addr, subsistence + 100);
- Balances::reserve(&addr, subsistence + 100).unwrap();
- let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, vec![])
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::TransferFailed);
- });
-}
-
-#[test]
-fn call_return_code() {
- let (caller_code, caller_hash) = compile_module::<Test>("call_return_code").unwrap();
- let (callee_code, callee_hash) = compile_module::<Test>("ok_trap_revert").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
- let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- caller_code,
- vec![0],
- vec![],
- ),);
- let addr_bob = Contracts::contract_address(&ALICE, &caller_hash, &[]);
- Balances::make_free_balance_be(&addr_bob, subsistence);
-
- // Contract calls into Django which is no valid contract
- let result = Contracts::bare_call(
- ALICE,
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::NotCallable);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(CHARLIE),
- subsistence * 100,
- GAS_LIMIT,
- callee_code,
- vec![0],
- vec![],
- ),);
- let addr_django = Contracts::contract_address(&CHARLIE, &callee_hash, &[]);
- Balances::make_free_balance_be(&addr_django, subsistence);
-
- // Contract has only the minimal balance so any transfer will return BelowSubsistence.
- let result = Contracts::bare_call(
- ALICE,
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- AsRef::<[u8]>::as_ref(&addr_django)
- .iter()
- .chain(&0u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
-
- // Contract has enough total balance in order to not go below the subsistence
- // threshold when transfering 100 balance but this balance is reserved so
- // the transfer still fails but with another return code.
- Balances::make_free_balance_be(&addr_bob, subsistence + 100);
- Balances::reserve(&addr_bob, subsistence + 100).unwrap();
- let result = Contracts::bare_call(
- ALICE,
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- AsRef::<[u8]>::as_ref(&addr_django)
- .iter()
- .chain(&0u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::TransferFailed);
-
- // Contract has enough balance but callee reverts because "1" is passed.
- Balances::make_free_balance_be(&addr_bob, subsistence + 1000);
- let result = Contracts::bare_call(
- ALICE,
- addr_bob.clone(),
- 0,
- GAS_LIMIT,
- AsRef::<[u8]>::as_ref(&addr_django)
- .iter()
- .chain(&1u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
-
- // Contract has enough balance but callee traps because "2" is passed.
- let result = Contracts::bare_call(
- ALICE,
- addr_bob,
- 0,
- GAS_LIMIT,
- AsRef::<[u8]>::as_ref(&addr_django)
- .iter()
- .chain(&2u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
- });
-}
-
-#[test]
-fn instantiate_return_code() {
- let (caller_code, caller_hash) = compile_module::<Test>("instantiate_return_code").unwrap();
- let (callee_code, callee_hash) = compile_module::<Test>("ok_trap_revert").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
- let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence);
- let callee_hash = callee_hash.as_ref().to_vec();
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- callee_code,
- vec![],
- vec![],
- ),);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- caller_code,
- vec![],
- vec![],
- ),);
- let addr = Contracts::contract_address(&ALICE, &caller_hash, &[]);
-
- // Contract has only the minimal balance so any transfer will return BelowSubsistence.
- Balances::make_free_balance_be(&addr, subsistence);
- let result =
- Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone())
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
-
- // Contract has enough total balance in order to not go below the subsistence
- // threshold when transfering the balance but this balance is reserved so
- // the transfer still fails but with another return code.
- Balances::make_free_balance_be(&addr, subsistence + 10_000);
- Balances::reserve(&addr, subsistence + 10_000).unwrap();
- let result =
- Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone())
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::TransferFailed);
-
- // Contract has enough balance but the passed code hash is invalid
- Balances::make_free_balance_be(&addr, subsistence + 10_000);
- let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0; 33])
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
-
- // Contract has enough balance but callee reverts because "1" is passed.
- let result = Contracts::bare_call(
- ALICE,
- addr.clone(),
- 0,
- GAS_LIMIT,
- callee_hash
- .iter()
- .chain(&1u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
-
- // Contract has enough balance but callee traps because "2" is passed.
- let result = Contracts::bare_call(
- ALICE,
- addr,
- 0,
- GAS_LIMIT,
- callee_hash
- .iter()
- .chain(&2u32.to_le_bytes())
- .cloned()
- .collect(),
- )
- .result
- .unwrap();
- assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
- });
-}
-
-#[test]
-fn disabled_chain_extension_wont_deploy() {
- let (code, _hash) = compile_module::<Test>("chain_extension").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
- TestExtension::disable();
- assert_err_ignore_postinfo!(
- Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 3 * subsistence,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),
- "module uses chain extensions but chain extensions are disabled",
- );
- });
-}
-
-#[test]
-fn disabled_chain_extension_errors_on_call() {
- let (code, hash) = compile_module::<Test>("chain_extension").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
- TestExtension::disable();
- assert_err_ignore_postinfo!(
- Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),
- Error::<Test>::NoChainExtension,
- );
- });
-}
-
-#[test]
-fn chain_extension_works() {
- let (code, hash) = compile_module::<Test>("chain_extension").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
-
- // The contract takes a up to 2 byte buffer where the first byte passed is used as
- // as func_id to the chain extension which behaves differently based on the
- // func_id.
-
- // 0 = read input buffer and pass it through as output
- let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0, 99]);
- let gas_consumed = result.gas_consumed;
- assert_eq!(TestExtension::last_seen_buffer(), vec![0, 99]);
- assert_eq!(result.result.unwrap().data, Bytes(vec![0, 99]));
-
- // 1 = treat inputs as integer primitives and store the supplied integers
- Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![1])
- .result
- .unwrap();
- // those values passed in the fixture
- assert_eq!(TestExtension::last_seen_inputs(), (4, 1, 16, 12));
-
- // 2 = charge some extra weight (amount supplied in second byte)
- let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![2, 42]);
- assert_ok!(result.result);
- assert_eq!(result.gas_consumed, gas_consumed + 42);
-
- // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer
- let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![3])
- .result
- .unwrap();
- assert_eq!(result.flags, ReturnFlags::REVERT);
- assert_eq!(result.data, Bytes(vec![42, 99]));
- });
-}
-
-#[test]
-fn lazy_removal_works() {
- let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
-
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
- let info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let trie = &info.child_trie_info();
-
- // Put value into the contracts child trie
- child::put(trie, &[99], &42);
-
- // Terminate the contract
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- vec![],
- ));
-
- // Contract info should be gone
- assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-
- // But value should be still there as the lazy removal did not run, yet.
- assert_matches!(child::get(trie, &[99]), Some(42));
-
- // Run the lazy removal
- Contracts::on_initialize(Weight::max_value());
-
- // Value should be gone now
- assert_matches!(child::get::<i32>(trie, &[99]), None);
- });
-}
-
-#[test]
-fn lazy_removal_partial_remove_works() {
- let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
-
- // We create a contract with some extra keys above the weight limit
- let extra_keys = 7u32;
- let weight_limit = 5_000_000_000;
- let (_, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);
- let vals: Vec<_> = (0..max_keys + extra_keys)
- .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
- .collect();
-
- let mut ext = ExtBuilder::default().existential_deposit(50).build();
-
- let trie = ext.execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
-
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
- let info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let trie = &info.child_trie_info();
-
- // Put value into the contracts child trie
- for val in &vals {
- Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();
- }
-
- // Terminate the contract
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- vec![],
- ));
-
- // Contract info should be gone
- assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-
- // But value should be still there as the lazy removal did not run, yet.
- for val in &vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
- }
-
- trie.clone()
- });
-
- // The lazy removal limit only applies to the backend but not to the overlay.
- // This commits all keys from the overlay to the backend.
- ext.commit_all().unwrap();
-
- ext.execute_with(|| {
- // Run the lazy removal
- let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);
-
- // Weight should be exhausted because we could not even delete all keys
- assert_eq!(weight_used, weight_limit);
-
- let mut num_deleted = 0u32;
- let mut num_remaining = 0u32;
-
- for val in &vals {
- match child::get::<u32>(&trie, &blake2_256(&val.0)) {
- None => num_deleted += 1,
- Some(x) if x == val.1 => num_remaining += 1,
- Some(_) => panic!("Unexpected value in contract storage"),
- }
- }
-
- // All but one key is removed
- assert_eq!(num_deleted + num_remaining, vals.len() as u32);
- assert_eq!(num_deleted, max_keys);
- assert_eq!(num_remaining, extra_keys);
- });
-}
-
-#[test]
-fn lazy_removal_does_no_run_on_full_block() {
- let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
-
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
- let info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let trie = &info.child_trie_info();
- let max_keys = 30;
-
- // Create some storage items for the contract.
- let vals: Vec<_> = (0..max_keys)
- .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
- .collect();
-
- // Put value into the contracts child trie
- for val in &vals {
- Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();
- }
-
- // Terminate the contract
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- vec![],
- ));
-
- // Contract info should be gone
- assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-
- // But value should be still there as the lazy removal did not run, yet.
- for val in &vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
- }
-
- // Fill up the block which should prevent the lazy storage removal from running.
- System::register_extra_weight_unchecked(
- <Test as system::Config>::BlockWeights::get().max_block,
- DispatchClass::Mandatory,
- );
-
- // Run the lazy removal without any limit so that all keys would be removed if there
- // had been some weight left in the block.
- let weight_used = Contracts::on_initialize(Weight::max_value());
- let base = <<Test as Config>::WeightInfo as WeightInfo>::on_initialize();
- assert_eq!(weight_used, base);
-
- // All the keys are still in place
- for val in &vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
- }
-
- // Run the lazy removal directly which disregards the block limits
- Storage::<Test>::process_deletion_queue_batch(Weight::max_value());
-
- // Now the keys should be gone
- for val in &vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);
- }
- });
-}
-
-#[test]
-fn lazy_removal_does_not_use_all_weight() {
- let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
-
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
- let info = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let trie = &info.child_trie_info();
- let weight_limit = 5_000_000_000;
- let (weight_per_key, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);
-
- // We create a contract with one less storage item than we can remove within the limit
- let vals: Vec<_> = (0..max_keys - 1)
- .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))
- .collect();
-
- // Put value into the contracts child trie
- for val in &vals {
- Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();
- }
-
- // Terminate the contract
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr.clone(),
- 0,
- GAS_LIMIT,
- vec![],
- ));
-
- // Contract info should be gone
- assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));
-
- // But value should be still there as the lazy removal did not run, yet.
- for val in &vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));
- }
-
- // Run the lazy removal
- let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);
-
- // We have one less key in our trie than our weight limit suffices for
- assert_eq!(weight_used, weight_limit - weight_per_key);
-
- // All the keys are removed
- for val in vals {
- assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);
- }
- });
-}
-
-#[test]
-fn deletion_queue_full() {
- let (code, hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code,
- vec![],
- vec![],
- ),);
-
- let addr = Contracts::contract_address(&ALICE, &hash, &[]);
-
- // fill the deletion queue up until its limit
- Storage::<Test>::fill_queue_with_dummies();
-
- // Terminate the contract should fail
- assert_err_ignore_postinfo!(
- Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),
- Error::<Test>::DeletionQueueFull,
- );
-
- // Contract should be alive because removal failed
- <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
-
- // make the contract ripe for eviction
- initialize_block(5);
-
- // eviction should fail for the same reason as termination
- assert_err!(
- Contracts::claim_surcharge(Origin::none(), addr.clone(), Some(ALICE)),
- Error::<Test>::DeletionQueueFull,
- );
-
- // Contract should be alive because removal failed
- <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- });
-}
-
-#[test]
-fn not_deployed_if_endowment_too_low_for_first_rent() {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
-
- // The instantiation deducted the rent for one block immediately
- let first_rent = <Test as Config>::RentFraction::get()
- // base_deposit + deploy_set_storage (4 bytes in 1 item) - free_balance
- .mul_ceil(80_000u32 + 40_000 + 10_000 - 30_000)
- // blocks to rent
- * 1;
-
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- // Create
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_storage_noop!(assert_err_ignore_postinfo!(
- Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- (BalanceOf::<Test>::from(first_rent) - BalanceOf::<Test>::from(1u32)).encode(), // rent allowance
- vec![],
- ),
- Error::<Test>::NewContractNotFunded,
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- assert_matches!(ContractInfoOf::<Test>::get(&addr), None);
- });
-}
-
-#[test]
-fn surcharge_reward_is_capped() {
- let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- 30_000,
- GAS_LIMIT,
- wasm,
- <BalanceOf<Test>>::from(10_000u32).encode(), // rent allowance
- vec![],
- ));
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
- let contract = <ContractInfoOf<Test>>::get(&addr)
- .unwrap()
- .get_alive()
- .unwrap();
- let balance = Balances::free_balance(&ALICE);
- let reward = <Test as Config>::SurchargeReward::get();
-
- // some rent should have payed due to instantiation
- assert_ne!(contract.rent_payed, 0);
-
- // the reward should be parameterized sufficiently high to make this test useful
- assert!(reward > contract.rent_payed);
-
- // make contract eligible for eviction
- initialize_block(40);
-
- // this should have removed the contract
- assert_ok!(Contracts::claim_surcharge(
- Origin::none(),
- addr.clone(),
- Some(ALICE)
- ));
-
- // this reward does not take into account the last rent payment collected during eviction
- let capped_reward = reward.min(contract.rent_payed);
-
- // this is smaller than the actual reward because it does not take into account the
- // rent collected during eviction
- assert!(Balances::free_balance(&ALICE) > balance + capped_reward);
-
- // the full reward is not payed out because of the cap introduced by rent_payed
- assert!(Balances::free_balance(&ALICE) < balance + reward);
- });
-}
-
-#[test]
-fn refcounter() {
- let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- let subsistence = Pallet::<Test>::subsistence_threshold();
-
- // Create two contracts with the same code and check that they do in fact share it.
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- wasm.clone(),
- vec![],
- vec![0],
- ));
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- wasm.clone(),
- vec![],
- vec![1],
- ));
- assert_refcount!(code_hash, 2);
-
- // Sharing should also work with the usual instantiate call
- assert_ok!(Contracts::instantiate(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- code_hash,
- vec![],
- vec![2],
- ));
- assert_refcount!(code_hash, 3);
-
- // addresses of all three existing contracts
- let addr0 = Contracts::contract_address(&ALICE, &code_hash, &[0]);
- let addr1 = Contracts::contract_address(&ALICE, &code_hash, &[1]);
- let addr2 = Contracts::contract_address(&ALICE, &code_hash, &[2]);
-
- // Terminating one contract should decrement the refcount
- assert_ok!(Contracts::call(
- Origin::signed(ALICE),
- addr0,
- 0,
- GAS_LIMIT,
- vec![],
- ));
- assert_refcount!(code_hash, 2);
-
- // make remaining contracts eligible for eviction
- initialize_block(40);
-
- // remove one of them
- assert_ok!(Contracts::claim_surcharge(
- Origin::none(),
- addr1,
- Some(ALICE)
- ));
- assert_refcount!(code_hash, 1);
-
- // Pristine code should still be there
- crate::PristineCode::<Test>::get(code_hash).unwrap();
-
- // remove the last contract
- assert_ok!(Contracts::claim_surcharge(
- Origin::none(),
- addr2,
- Some(ALICE)
- ));
- assert_refcount!(code_hash, 0);
-
- // all code should be gone
- assert_matches!(crate::PristineCode::<Test>::get(code_hash), None);
- assert_matches!(crate::CodeStorage::<Test>::get(code_hash), None);
- });
-}
-
-#[test]
-fn reinstrument_does_charge() {
- let (wasm, code_hash) = compile_module::<Test>("return_with_data").unwrap();
- ExtBuilder::default()
- .existential_deposit(50)
- .build()
- .execute_with(|| {
- let _ = Balances::deposit_creating(&ALICE, 1_000_000);
- let subsistence = Pallet::<Test>::subsistence_threshold();
- let zero = 0u32.to_le_bytes().encode();
- let code_len = wasm.len() as u32;
-
- assert_ok!(Contracts::instantiate_with_code(
- Origin::signed(ALICE),
- subsistence * 100,
- GAS_LIMIT,
- wasm,
- zero.clone(),
- vec![],
- ));
-
- let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
-
- // Call the contract two times without reinstrument
-
- let result0 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());
- assert!(result0.result.unwrap().is_success());
-
- let result1 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());
- assert!(result1.result.unwrap().is_success());
-
- // They should match because both where called with the same schedule.
- assert_eq!(result0.gas_consumed, result1.gas_consumed);
-
- // Update the schedule version but keep the rest the same
- crate::CurrentSchedule::mutate(|old: &mut Schedule<Test>| {
- old.version += 1;
- });
-
- // This call should trigger reinstrumentation
- let result2 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());
- assert!(result2.result.unwrap().is_success());
- assert!(result2.gas_consumed > result1.gas_consumed);
- assert_eq!(
- result2.gas_consumed,
- result1.gas_consumed + <Test as Config>::WeightInfo::instrument(code_len / 1024),
- );
- });
-}
pallets/contracts/src/weights.rsdiffbeforeafterboth--- a/pallets/contracts/src/weights.rs
+++ /dev/null
@@ -1,1417 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) 2021 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.
-
-//! Autogenerated weights for pallet_contracts
-//!
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
-//! DATE: 2021-02-18, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
-//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
-
-// Executed Command:
-// target/release/substrate
-// benchmark
-// --chain=dev
-// --steps=50
-// --repeat=20
-// --pallet=pallet_contracts
-// --extrinsic=*
-// --execution=wasm
-// --wasm-execution=compiled
-// --heap-pages=4096
-// --output=./frame/contracts/src/weights.rs
-// --template=./.maintain/frame-weight-template.hbs
-
-#![allow(unused_parens)]
-#![allow(unused_imports)]
-
-use frame_support::{
- traits::Get,
- weights::{Weight, constants::RocksDbWeight},
-};
-use sp_std::marker::PhantomData;
-
-/// Weight functions needed for pallet_contracts.
-pub trait WeightInfo {
- fn on_initialize() -> Weight;
- fn on_initialize_per_trie_key(k: u32) -> Weight;
- fn on_initialize_per_queue_item(q: u32) -> Weight;
- fn instrument(c: u32) -> Weight;
- fn update_schedule() -> Weight;
- fn instantiate_with_code(c: u32, s: u32) -> Weight;
- fn instantiate(c: u32, s: u32) -> Weight;
- fn call(c: u32) -> Weight;
- fn claim_surcharge(c: u32) -> Weight;
- fn seal_caller(r: u32) -> Weight;
- fn seal_address(r: u32) -> Weight;
- fn seal_gas_left(r: u32) -> Weight;
- fn seal_balance(r: u32) -> Weight;
- fn seal_value_transferred(r: u32) -> Weight;
- fn seal_minimum_balance(r: u32) -> Weight;
- fn seal_tombstone_deposit(r: u32) -> Weight;
- fn seal_rent_allowance(r: u32) -> Weight;
- fn seal_block_number(r: u32) -> Weight;
- fn seal_now(r: u32) -> Weight;
- fn seal_rent_params(r: u32) -> Weight;
- fn seal_weight_to_fee(r: u32) -> Weight;
- fn seal_gas(r: u32) -> Weight;
- fn seal_input(r: u32) -> Weight;
- fn seal_input_per_kb(n: u32) -> Weight;
- fn seal_return(r: u32) -> Weight;
- fn seal_return_per_kb(n: u32) -> Weight;
- fn seal_terminate(r: u32) -> Weight;
- fn seal_terminate_per_code_kb(c: u32) -> Weight;
- fn seal_restore_to(r: u32) -> Weight;
- fn seal_restore_to_per_code_kb_delta(c: u32, t: u32, d: u32) -> Weight;
- fn seal_random(r: u32) -> Weight;
- fn seal_deposit_event(r: u32) -> Weight;
- fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32) -> Weight;
- fn seal_set_rent_allowance(r: u32) -> Weight;
- fn seal_set_storage(r: u32) -> Weight;
- fn seal_set_storage_per_kb(n: u32) -> Weight;
- fn seal_clear_storage(r: u32) -> Weight;
- fn seal_get_storage(r: u32) -> Weight;
- fn seal_get_storage_per_kb(n: u32) -> Weight;
- fn seal_transfer(r: u32) -> Weight;
- fn seal_call(r: u32) -> Weight;
- fn seal_call_per_code_transfer_input_output_kb(c: u32, t: u32, i: u32, o: u32) -> Weight;
- fn seal_instantiate(r: u32) -> Weight;
- fn seal_instantiate_per_code_input_output_salt_kb(c: u32, i: u32, o: u32, s: u32) -> Weight;
- fn seal_hash_sha2_256(r: u32) -> Weight;
- fn seal_hash_sha2_256_per_kb(n: u32) -> Weight;
- fn seal_hash_keccak_256(r: u32) -> Weight;
- fn seal_hash_keccak_256_per_kb(n: u32) -> Weight;
- fn seal_hash_blake2_256(r: u32) -> Weight;
- fn seal_hash_blake2_256_per_kb(n: u32) -> Weight;
- fn seal_hash_blake2_128(r: u32) -> Weight;
- fn seal_hash_blake2_128_per_kb(n: u32) -> Weight;
- fn instr_i64const(r: u32) -> Weight;
- fn instr_i64load(r: u32) -> Weight;
- fn instr_i64store(r: u32) -> Weight;
- fn instr_select(r: u32) -> Weight;
- fn instr_if(r: u32) -> Weight;
- fn instr_br(r: u32) -> Weight;
- fn instr_br_if(r: u32) -> Weight;
- fn instr_br_table(r: u32) -> Weight;
- fn instr_br_table_per_entry(e: u32) -> Weight;
- fn instr_call(r: u32) -> Weight;
- fn instr_call_indirect(r: u32) -> Weight;
- fn instr_call_indirect_per_param(p: u32) -> Weight;
- fn instr_local_get(r: u32) -> Weight;
- fn instr_local_set(r: u32) -> Weight;
- fn instr_local_tee(r: u32) -> Weight;
- fn instr_global_get(r: u32) -> Weight;
- fn instr_global_set(r: u32) -> Weight;
- fn instr_memory_current(r: u32) -> Weight;
- fn instr_memory_grow(r: u32) -> Weight;
- fn instr_i64clz(r: u32) -> Weight;
- fn instr_i64ctz(r: u32) -> Weight;
- fn instr_i64popcnt(r: u32) -> Weight;
- fn instr_i64eqz(r: u32) -> Weight;
- fn instr_i64extendsi32(r: u32) -> Weight;
- fn instr_i64extendui32(r: u32) -> Weight;
- fn instr_i32wrapi64(r: u32) -> Weight;
- fn instr_i64eq(r: u32) -> Weight;
- fn instr_i64ne(r: u32) -> Weight;
- fn instr_i64lts(r: u32) -> Weight;
- fn instr_i64ltu(r: u32) -> Weight;
- fn instr_i64gts(r: u32) -> Weight;
- fn instr_i64gtu(r: u32) -> Weight;
- fn instr_i64les(r: u32) -> Weight;
- fn instr_i64leu(r: u32) -> Weight;
- fn instr_i64ges(r: u32) -> Weight;
- fn instr_i64geu(r: u32) -> Weight;
- fn instr_i64add(r: u32) -> Weight;
- fn instr_i64sub(r: u32) -> Weight;
- fn instr_i64mul(r: u32) -> Weight;
- fn instr_i64divs(r: u32) -> Weight;
- fn instr_i64divu(r: u32) -> Weight;
- fn instr_i64rems(r: u32) -> Weight;
- fn instr_i64remu(r: u32) -> Weight;
- fn instr_i64and(r: u32) -> Weight;
- fn instr_i64or(r: u32) -> Weight;
- fn instr_i64xor(r: u32) -> Weight;
- fn instr_i64shl(r: u32) -> Weight;
- fn instr_i64shrs(r: u32) -> Weight;
- fn instr_i64shru(r: u32) -> Weight;
- fn instr_i64rotl(r: u32) -> Weight;
- fn instr_i64rotr(r: u32) -> Weight;
-}
-
-/// Weights for pallet_contracts using the Substrate node and recommended hardware.
-pub struct SubstrateWeight<T>(PhantomData<T>);
-impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
- fn on_initialize() -> Weight {
- (3_850_000 as Weight).saturating_add(T::DbWeight::get().reads(1 as Weight))
- }
- fn on_initialize_per_trie_key(k: u32) -> Weight {
- (52_925_000 as Weight)
- // Standard Error: 5_000
- .saturating_add((2_297_000 as Weight).saturating_mul(k as Weight))
- .saturating_add(T::DbWeight::get().reads(1 as Weight))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(k as Weight)))
- }
- fn on_initialize_per_queue_item(q: u32) -> Weight {
- (434_698_000 as Weight)
- // Standard Error: 210_000
- .saturating_add((166_559_000 as Weight).saturating_mul(q as Weight))
- .saturating_add(T::DbWeight::get().reads(1 as Weight))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- }
- fn instrument(c: u32) -> Weight {
- (29_918_000 as Weight)
- // Standard Error: 185_000
- .saturating_add((123_774_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(T::DbWeight::get().reads(1 as Weight))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- }
- fn update_schedule() -> Weight {
- (29_795_000 as Weight)
- .saturating_add(T::DbWeight::get().reads(1 as Weight))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- }
- fn instantiate_with_code(c: u32, s: u32) -> Weight {
- (225_834_000 as Weight)
- // Standard Error: 144_000
- .saturating_add((165_632_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 9_000
- .saturating_add((2_563_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- .saturating_add(T::DbWeight::get().writes(5 as Weight))
- }
- fn instantiate(c: u32, s: u32) -> Weight {
- (190_482_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((8_724_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 1_000
- .saturating_add((2_512_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- .saturating_add(T::DbWeight::get().writes(4 as Weight))
- }
- fn call(c: u32) -> Weight {
- (195_414_000 as Weight)
- // Standard Error: 2_000
- .saturating_add((3_920_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().writes(2 as Weight))
- }
- fn claim_surcharge(c: u32) -> Weight {
- (336_867_000 as Weight)
- // Standard Error: 10_000
- .saturating_add((5_262_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().writes(4 as Weight))
- }
- fn seal_caller(r: u32) -> Weight {
- (143_935_000 as Weight)
- // Standard Error: 128_000
- .saturating_add((266_876_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_address(r: u32) -> Weight {
- (150_342_000 as Weight)
- // Standard Error: 127_000
- .saturating_add((266_051_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_gas_left(r: u32) -> Weight {
- (144_833_000 as Weight)
- // Standard Error: 124_000
- .saturating_add((259_279_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_balance(r: u32) -> Weight {
- (152_032_000 as Weight)
- // Standard Error: 218_000
- .saturating_add((573_038_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_value_transferred(r: u32) -> Weight {
- (148_831_000 as Weight)
- // Standard Error: 147_000
- .saturating_add((260_718_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_minimum_balance(r: u32) -> Weight {
- (142_925_000 as Weight)
- // Standard Error: 130_000
- .saturating_add((260_426_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_tombstone_deposit(r: u32) -> Weight {
- (143_151_000 as Weight)
- // Standard Error: 119_000
- .saturating_add((260_964_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_rent_allowance(r: u32) -> Weight {
- (155_126_000 as Weight)
- // Standard Error: 225_000
- .saturating_add((599_056_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_block_number(r: u32) -> Weight {
- (144_566_000 as Weight)
- // Standard Error: 110_000
- .saturating_add((257_620_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_now(r: u32) -> Weight {
- (147_274_000 as Weight)
- // Standard Error: 115_000
- .saturating_add((258_627_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_rent_params(r: u32) -> Weight {
- (168_575_000 as Weight)
- // Standard Error: 394_000
- .saturating_add((397_754_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_weight_to_fee(r: u32) -> Weight {
- (148_102_000 as Weight)
- // Standard Error: 201_000
- .saturating_add((537_088_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- }
- fn seal_gas(r: u32) -> Weight {
- (125_122_000 as Weight)
- // Standard Error: 89_000
- .saturating_add((122_350_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_input(r: u32) -> Weight {
- (137_334_000 as Weight)
- // Standard Error: 99_000
- .saturating_add((7_359_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_input_per_kb(n: u32) -> Weight {
- (145_094_000 as Weight)
- // Standard Error: 0
- .saturating_add((283_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_return(r: u32) -> Weight {
- (127_544_000 as Weight)
- // Standard Error: 138_000
- .saturating_add((4_640_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_return_per_kb(n: u32) -> Weight {
- (137_517_000 as Weight)
- // Standard Error: 0
- .saturating_add((783_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_terminate(r: u32) -> Weight {
- (138_292_000 as Weight)
- // Standard Error: 689_000
- .saturating_add((111_698_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes((5 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_terminate_per_code_kb(c: u32) -> Weight {
- (263_507_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((8_409_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(T::DbWeight::get().reads(7 as Weight))
- .saturating_add(T::DbWeight::get().writes(5 as Weight))
- }
- fn seal_restore_to(r: u32) -> Weight {
- (232_291_000 as Weight)
- // Standard Error: 301_000
- .saturating_add((136_379_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes((6 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_restore_to_per_code_kb_delta(c: u32, t: u32, d: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 162_000
- .saturating_add((8_619_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 162_000
- .saturating_add((4_877_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 1_433_000
- .saturating_add((3_762_810_000 as Weight).saturating_mul(d as Weight))
- .saturating_add(T::DbWeight::get().reads(8 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(d as Weight)))
- .saturating_add(T::DbWeight::get().writes(7 as Weight))
- .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(d as Weight)))
- }
- fn seal_random(r: u32) -> Weight {
- (153_634_000 as Weight)
- // Standard Error: 267_000
- .saturating_add((650_160_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- }
- fn seal_deposit_event(r: u32) -> Weight {
- (137_080_000 as Weight)
- // Standard Error: 1_009_000
- .saturating_add((949_228_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32) -> Weight {
- (1_259_129_000 as Weight)
- // Standard Error: 2_542_000
- .saturating_add((609_859_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 501_000
- .saturating_add((249_496_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(t as Weight)))
- .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(t as Weight)))
- }
- fn seal_set_rent_allowance(r: u32) -> Weight {
- (170_417_000 as Weight)
- // Standard Error: 434_000
- .saturating_add((721_511_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- }
- fn seal_set_storage(r: u32) -> Weight {
- (1_870_542_000 as Weight)
- // Standard Error: 26_871_000
- .saturating_add((18_312_239_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_set_storage_per_kb(n: u32) -> Weight {
- (1_763_732_000 as Weight)
- // Standard Error: 258_000
- .saturating_add((74_848_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- .saturating_add(T::DbWeight::get().writes(2 as Weight))
- }
- fn seal_clear_storage(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 2_745_000
- .saturating_add((2_316_433_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_get_storage(r: u32) -> Weight {
- (87_218_000 as Weight)
- // Standard Error: 745_000
- .saturating_add((948_121_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_get_storage_per_kb(n: u32) -> Weight {
- (719_050_000 as Weight)
- // Standard Error: 266_000
- .saturating_add((154_812_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- }
- fn seal_transfer(r: u32) -> Weight {
- (19_439_000 as Weight)
- // Standard Error: 2_468_000
- .saturating_add((5_674_822_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- .saturating_add(T::DbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes(1 as Weight))
- .saturating_add(T::DbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_call(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 7_465_000
- .saturating_add((11_066_530_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- .saturating_add(T::DbWeight::get().reads((200 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_call_per_code_transfer_input_output_kb(c: u32, t: u32, i: u32, o: u32) -> Weight {
- (9_916_288_000 as Weight)
- // Standard Error: 552_000
- .saturating_add((397_842_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 229_902_000
- .saturating_add((5_243_673_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 72_000
- .saturating_add((59_737_000 as Weight).saturating_mul(i as Weight))
- // Standard Error: 77_000
- .saturating_add((82_259_000 as Weight).saturating_mul(o as Weight))
- .saturating_add(T::DbWeight::get().reads(206 as Weight))
- .saturating_add(T::DbWeight::get().writes((101 as Weight).saturating_mul(t as Weight)))
- }
- fn seal_instantiate(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 32_016_000
- .saturating_add((22_206_489_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(6 as Weight))
- .saturating_add(T::DbWeight::get().reads((300 as Weight).saturating_mul(r as Weight)))
- .saturating_add(T::DbWeight::get().writes(2 as Weight))
- .saturating_add(T::DbWeight::get().writes((300 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_instantiate_per_code_input_output_salt_kb(c: u32, i: u32, o: u32, s: u32) -> Weight {
- (9_991_947_000 as Weight)
- // Standard Error: 637_000
- .saturating_add((881_981_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 90_000
- .saturating_add((63_638_000 as Weight).saturating_mul(i as Weight))
- // Standard Error: 90_000
- .saturating_add((87_288_000 as Weight).saturating_mul(o as Weight))
- // Standard Error: 90_000
- .saturating_add((311_808_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(T::DbWeight::get().reads(207 as Weight))
- .saturating_add(T::DbWeight::get().writes(203 as Weight))
- }
- fn seal_hash_sha2_256(r: u32) -> Weight {
- (132_452_000 as Weight)
- // Standard Error: 227_000
- .saturating_add((239_671_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_sha2_256_per_kb(n: u32) -> Weight {
- (756_802_000 as Weight)
- // Standard Error: 48_000
- .saturating_add((429_454_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_keccak_256(r: u32) -> Weight {
- (139_440_000 as Weight)
- // Standard Error: 128_000
- .saturating_add((249_514_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_keccak_256_per_kb(n: u32) -> Weight {
- (658_595_000 as Weight)
- // Standard Error: 35_000
- .saturating_add((343_814_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_256(r: u32) -> Weight {
- (138_124_000 as Weight)
- // Standard Error: 140_000
- .saturating_add((223_189_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_256_per_kb(n: u32) -> Weight {
- (689_667_000 as Weight)
- // Standard Error: 41_000
- .saturating_add((160_006_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_128(r: u32) -> Weight {
- (140_225_000 as Weight)
- // Standard Error: 156_000
- .saturating_add((223_696_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_128_per_kb(n: u32) -> Weight {
- (693_756_000 as Weight)
- // Standard Error: 40_000
- .saturating_add((159_996_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(T::DbWeight::get().reads(5 as Weight))
- }
- fn instr_i64const(r: u32) -> Weight {
- (24_250_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((3_134_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64load(r: u32) -> Weight {
- (26_509_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((161_556_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64store(r: u32) -> Weight {
- (26_499_000 as Weight)
- // Standard Error: 59_000
- .saturating_add((233_755_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_select(r: u32) -> Weight {
- (24_175_000 as Weight)
- // Standard Error: 16_000
- .saturating_add((12_450_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_if(r: u32) -> Weight {
- (24_219_000 as Weight)
- // Standard Error: 26_000
- .saturating_add((12_058_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br(r: u32) -> Weight {
- (24_146_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((6_017_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_if(r: u32) -> Weight {
- (24_229_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((13_726_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_table(r: u32) -> Weight {
- (24_219_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((15_115_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_table_per_entry(e: u32) -> Weight {
- (34_981_000 as Weight)
- // Standard Error: 1_000
- .saturating_add((156_000 as Weight).saturating_mul(e as Weight))
- }
- fn instr_call(r: u32) -> Weight {
- (24_599_000 as Weight)
- // Standard Error: 102_000
- .saturating_add((95_771_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_call_indirect(r: u32) -> Weight {
- (32_584_000 as Weight)
- // Standard Error: 176_000
- .saturating_add((193_216_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_call_indirect_per_param(p: u32) -> Weight {
- (240_739_000 as Weight)
- // Standard Error: 6_000
- .saturating_add((3_407_000 as Weight).saturating_mul(p as Weight))
- }
- fn instr_local_get(r: u32) -> Weight {
- (41_963_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((3_110_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_local_set(r: u32) -> Weight {
- (41_956_000 as Weight)
- // Standard Error: 9_000
- .saturating_add((3_460_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_local_tee(r: u32) -> Weight {
- (42_002_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((4_591_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_global_get(r: u32) -> Weight {
- (27_646_000 as Weight)
- // Standard Error: 23_000
- .saturating_add((7_821_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_global_set(r: u32) -> Weight {
- (27_615_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((11_807_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_memory_current(r: u32) -> Weight {
- (27_106_000 as Weight)
- // Standard Error: 78_000
- .saturating_add((2_952_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_memory_grow(r: u32) -> Weight {
- (24_956_000 as Weight)
- // Standard Error: 3_541_000
- .saturating_add((2_332_414_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64clz(r: u32) -> Weight {
- (24_183_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((5_166_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ctz(r: u32) -> Weight {
- (24_142_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((5_146_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64popcnt(r: u32) -> Weight {
- (24_161_000 as Weight)
- // Standard Error: 23_000
- .saturating_add((5_807_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64eqz(r: u32) -> Weight {
- (24_167_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((5_288_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64extendsi32(r: u32) -> Weight {
- (24_252_000 as Weight)
- // Standard Error: 9_000
- .saturating_add((5_091_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64extendui32(r: u32) -> Weight {
- (24_243_000 as Weight)
- // Standard Error: 16_000
- .saturating_add((5_076_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i32wrapi64(r: u32) -> Weight {
- (24_227_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((5_135_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64eq(r: u32) -> Weight {
- (24_278_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((7_124_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ne(r: u32) -> Weight {
- (24_254_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_067_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64lts(r: u32) -> Weight {
- (24_220_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((7_122_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ltu(r: u32) -> Weight {
- (24_221_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_221_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64gts(r: u32) -> Weight {
- (24_259_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_135_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64gtu(r: u32) -> Weight {
- (24_245_000 as Weight)
- // Standard Error: 10_000
- .saturating_add((7_193_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64les(r: u32) -> Weight {
- (24_289_000 as Weight)
- // Standard Error: 22_000
- .saturating_add((7_023_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64leu(r: u32) -> Weight {
- (24_239_000 as Weight)
- // Standard Error: 21_000
- .saturating_add((7_065_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ges(r: u32) -> Weight {
- (24_256_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_119_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64geu(r: u32) -> Weight {
- (24_240_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((7_225_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64add(r: u32) -> Weight {
- (24_266_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((6_996_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64sub(r: u32) -> Weight {
- (24_265_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((6_974_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64mul(r: u32) -> Weight {
- (24_232_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((7_103_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64divs(r: u32) -> Weight {
- (24_245_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((12_915_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64divu(r: u32) -> Weight {
- (24_177_000 as Weight)
- // Standard Error: 21_000
- .saturating_add((12_232_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rems(r: u32) -> Weight {
- (24_171_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((12_939_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64remu(r: u32) -> Weight {
- (24_788_000 as Weight)
- // Standard Error: 22_000
- .saturating_add((11_657_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64and(r: u32) -> Weight {
- (24_252_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_003_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64or(r: u32) -> Weight {
- (24_263_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((7_005_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64xor(r: u32) -> Weight {
- (24_239_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((7_020_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shl(r: u32) -> Weight {
- (24_212_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_172_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shrs(r: u32) -> Weight {
- (24_220_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((7_246_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shru(r: u32) -> Weight {
- (24_213_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((7_191_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rotl(r: u32) -> Weight {
- (24_221_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((7_192_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rotr(r: u32) -> Weight {
- (24_235_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((7_106_000 as Weight).saturating_mul(r as Weight))
- }
-}
-
-// For backwards compatibility and tests
-impl WeightInfo for () {
- fn on_initialize() -> Weight {
- (3_850_000 as Weight).saturating_add(RocksDbWeight::get().reads(1 as Weight))
- }
- fn on_initialize_per_trie_key(k: u32) -> Weight {
- (52_925_000 as Weight)
- // Standard Error: 5_000
- .saturating_add((2_297_000 as Weight).saturating_mul(k as Weight))
- .saturating_add(RocksDbWeight::get().reads(1 as Weight))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(k as Weight)))
- }
- fn on_initialize_per_queue_item(q: u32) -> Weight {
- (434_698_000 as Weight)
- // Standard Error: 210_000
- .saturating_add((166_559_000 as Weight).saturating_mul(q as Weight))
- .saturating_add(RocksDbWeight::get().reads(1 as Weight))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- }
- fn instrument(c: u32) -> Weight {
- (29_918_000 as Weight)
- // Standard Error: 185_000
- .saturating_add((123_774_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(RocksDbWeight::get().reads(1 as Weight))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- }
- fn update_schedule() -> Weight {
- (29_795_000 as Weight)
- .saturating_add(RocksDbWeight::get().reads(1 as Weight))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- }
- fn instantiate_with_code(c: u32, s: u32) -> Weight {
- (225_834_000 as Weight)
- // Standard Error: 144_000
- .saturating_add((165_632_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 9_000
- .saturating_add((2_563_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- .saturating_add(RocksDbWeight::get().writes(5 as Weight))
- }
- fn instantiate(c: u32, s: u32) -> Weight {
- (190_482_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((8_724_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 1_000
- .saturating_add((2_512_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- .saturating_add(RocksDbWeight::get().writes(4 as Weight))
- }
- fn call(c: u32) -> Weight {
- (195_414_000 as Weight)
- // Standard Error: 2_000
- .saturating_add((3_920_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().writes(2 as Weight))
- }
- fn claim_surcharge(c: u32) -> Weight {
- (336_867_000 as Weight)
- // Standard Error: 10_000
- .saturating_add((5_262_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().writes(4 as Weight))
- }
- fn seal_caller(r: u32) -> Weight {
- (143_935_000 as Weight)
- // Standard Error: 128_000
- .saturating_add((266_876_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_address(r: u32) -> Weight {
- (150_342_000 as Weight)
- // Standard Error: 127_000
- .saturating_add((266_051_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_gas_left(r: u32) -> Weight {
- (144_833_000 as Weight)
- // Standard Error: 124_000
- .saturating_add((259_279_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_balance(r: u32) -> Weight {
- (152_032_000 as Weight)
- // Standard Error: 218_000
- .saturating_add((573_038_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_value_transferred(r: u32) -> Weight {
- (148_831_000 as Weight)
- // Standard Error: 147_000
- .saturating_add((260_718_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_minimum_balance(r: u32) -> Weight {
- (142_925_000 as Weight)
- // Standard Error: 130_000
- .saturating_add((260_426_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_tombstone_deposit(r: u32) -> Weight {
- (143_151_000 as Weight)
- // Standard Error: 119_000
- .saturating_add((260_964_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_rent_allowance(r: u32) -> Weight {
- (155_126_000 as Weight)
- // Standard Error: 225_000
- .saturating_add((599_056_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_block_number(r: u32) -> Weight {
- (144_566_000 as Weight)
- // Standard Error: 110_000
- .saturating_add((257_620_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_now(r: u32) -> Weight {
- (147_274_000 as Weight)
- // Standard Error: 115_000
- .saturating_add((258_627_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_rent_params(r: u32) -> Weight {
- (168_575_000 as Weight)
- // Standard Error: 394_000
- .saturating_add((397_754_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_weight_to_fee(r: u32) -> Weight {
- (148_102_000 as Weight)
- // Standard Error: 201_000
- .saturating_add((537_088_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- }
- fn seal_gas(r: u32) -> Weight {
- (125_122_000 as Weight)
- // Standard Error: 89_000
- .saturating_add((122_350_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_input(r: u32) -> Weight {
- (137_334_000 as Weight)
- // Standard Error: 99_000
- .saturating_add((7_359_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_input_per_kb(n: u32) -> Weight {
- (145_094_000 as Weight)
- // Standard Error: 0
- .saturating_add((283_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_return(r: u32) -> Weight {
- (127_544_000 as Weight)
- // Standard Error: 138_000
- .saturating_add((4_640_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_return_per_kb(n: u32) -> Weight {
- (137_517_000 as Weight)
- // Standard Error: 0
- .saturating_add((783_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_terminate(r: u32) -> Weight {
- (138_292_000 as Weight)
- // Standard Error: 689_000
- .saturating_add((111_698_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes((5 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_terminate_per_code_kb(c: u32) -> Weight {
- (263_507_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((8_409_000 as Weight).saturating_mul(c as Weight))
- .saturating_add(RocksDbWeight::get().reads(7 as Weight))
- .saturating_add(RocksDbWeight::get().writes(5 as Weight))
- }
- fn seal_restore_to(r: u32) -> Weight {
- (232_291_000 as Weight)
- // Standard Error: 301_000
- .saturating_add((136_379_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes((6 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_restore_to_per_code_kb_delta(c: u32, t: u32, d: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 162_000
- .saturating_add((8_619_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 162_000
- .saturating_add((4_877_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 1_433_000
- .saturating_add((3_762_810_000 as Weight).saturating_mul(d as Weight))
- .saturating_add(RocksDbWeight::get().reads(8 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(d as Weight)))
- .saturating_add(RocksDbWeight::get().writes(7 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((100 as Weight).saturating_mul(d as Weight)),
- )
- }
- fn seal_random(r: u32) -> Weight {
- (153_634_000 as Weight)
- // Standard Error: 267_000
- .saturating_add((650_160_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- }
- fn seal_deposit_event(r: u32) -> Weight {
- (137_080_000 as Weight)
- // Standard Error: 1_009_000
- .saturating_add((949_228_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_deposit_event_per_topic_and_kb(t: u32, n: u32) -> Weight {
- (1_259_129_000 as Weight)
- // Standard Error: 2_542_000
- .saturating_add((609_859_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 501_000
- .saturating_add((249_496_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(t as Weight)))
- .saturating_add(
- RocksDbWeight::get().writes((100 as Weight).saturating_mul(t as Weight)),
- )
- }
- fn seal_set_rent_allowance(r: u32) -> Weight {
- (170_417_000 as Weight)
- // Standard Error: 434_000
- .saturating_add((721_511_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- }
- fn seal_set_storage(r: u32) -> Weight {
- (1_870_542_000 as Weight)
- // Standard Error: 26_871_000
- .saturating_add((18_312_239_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)),
- )
- }
- fn seal_set_storage_per_kb(n: u32) -> Weight {
- (1_763_732_000 as Weight)
- // Standard Error: 258_000
- .saturating_add((74_848_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- .saturating_add(RocksDbWeight::get().writes(2 as Weight))
- }
- fn seal_clear_storage(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 2_745_000
- .saturating_add((2_316_433_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)),
- )
- }
- fn seal_get_storage(r: u32) -> Weight {
- (87_218_000 as Weight)
- // Standard Error: 745_000
- .saturating_add((948_121_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_get_storage_per_kb(n: u32) -> Weight {
- (719_050_000 as Weight)
- // Standard Error: 266_000
- .saturating_add((154_812_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- }
- fn seal_transfer(r: u32) -> Weight {
- (19_439_000 as Weight)
- // Standard Error: 2_468_000
- .saturating_add((5_674_822_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- .saturating_add(RocksDbWeight::get().reads((100 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes(1 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((100 as Weight).saturating_mul(r as Weight)),
- )
- }
- fn seal_call(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 7_465_000
- .saturating_add((11_066_530_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- .saturating_add(RocksDbWeight::get().reads((200 as Weight).saturating_mul(r as Weight)))
- }
- fn seal_call_per_code_transfer_input_output_kb(c: u32, t: u32, i: u32, o: u32) -> Weight {
- (9_916_288_000 as Weight)
- // Standard Error: 552_000
- .saturating_add((397_842_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 229_902_000
- .saturating_add((5_243_673_000 as Weight).saturating_mul(t as Weight))
- // Standard Error: 72_000
- .saturating_add((59_737_000 as Weight).saturating_mul(i as Weight))
- // Standard Error: 77_000
- .saturating_add((82_259_000 as Weight).saturating_mul(o as Weight))
- .saturating_add(RocksDbWeight::get().reads(206 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((101 as Weight).saturating_mul(t as Weight)),
- )
- }
- fn seal_instantiate(r: u32) -> Weight {
- (0 as Weight)
- // Standard Error: 32_016_000
- .saturating_add((22_206_489_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(6 as Weight))
- .saturating_add(RocksDbWeight::get().reads((300 as Weight).saturating_mul(r as Weight)))
- .saturating_add(RocksDbWeight::get().writes(2 as Weight))
- .saturating_add(
- RocksDbWeight::get().writes((300 as Weight).saturating_mul(r as Weight)),
- )
- }
- fn seal_instantiate_per_code_input_output_salt_kb(c: u32, i: u32, o: u32, s: u32) -> Weight {
- (9_991_947_000 as Weight)
- // Standard Error: 637_000
- .saturating_add((881_981_000 as Weight).saturating_mul(c as Weight))
- // Standard Error: 90_000
- .saturating_add((63_638_000 as Weight).saturating_mul(i as Weight))
- // Standard Error: 90_000
- .saturating_add((87_288_000 as Weight).saturating_mul(o as Weight))
- // Standard Error: 90_000
- .saturating_add((311_808_000 as Weight).saturating_mul(s as Weight))
- .saturating_add(RocksDbWeight::get().reads(207 as Weight))
- .saturating_add(RocksDbWeight::get().writes(203 as Weight))
- }
- fn seal_hash_sha2_256(r: u32) -> Weight {
- (132_452_000 as Weight)
- // Standard Error: 227_000
- .saturating_add((239_671_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_sha2_256_per_kb(n: u32) -> Weight {
- (756_802_000 as Weight)
- // Standard Error: 48_000
- .saturating_add((429_454_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_keccak_256(r: u32) -> Weight {
- (139_440_000 as Weight)
- // Standard Error: 128_000
- .saturating_add((249_514_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_keccak_256_per_kb(n: u32) -> Weight {
- (658_595_000 as Weight)
- // Standard Error: 35_000
- .saturating_add((343_814_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_256(r: u32) -> Weight {
- (138_124_000 as Weight)
- // Standard Error: 140_000
- .saturating_add((223_189_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_256_per_kb(n: u32) -> Weight {
- (689_667_000 as Weight)
- // Standard Error: 41_000
- .saturating_add((160_006_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_128(r: u32) -> Weight {
- (140_225_000 as Weight)
- // Standard Error: 156_000
- .saturating_add((223_696_000 as Weight).saturating_mul(r as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn seal_hash_blake2_128_per_kb(n: u32) -> Weight {
- (693_756_000 as Weight)
- // Standard Error: 40_000
- .saturating_add((159_996_000 as Weight).saturating_mul(n as Weight))
- .saturating_add(RocksDbWeight::get().reads(5 as Weight))
- }
- fn instr_i64const(r: u32) -> Weight {
- (24_250_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((3_134_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64load(r: u32) -> Weight {
- (26_509_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((161_556_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64store(r: u32) -> Weight {
- (26_499_000 as Weight)
- // Standard Error: 59_000
- .saturating_add((233_755_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_select(r: u32) -> Weight {
- (24_175_000 as Weight)
- // Standard Error: 16_000
- .saturating_add((12_450_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_if(r: u32) -> Weight {
- (24_219_000 as Weight)
- // Standard Error: 26_000
- .saturating_add((12_058_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br(r: u32) -> Weight {
- (24_146_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((6_017_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_if(r: u32) -> Weight {
- (24_229_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((13_726_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_table(r: u32) -> Weight {
- (24_219_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((15_115_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_br_table_per_entry(e: u32) -> Weight {
- (34_981_000 as Weight)
- // Standard Error: 1_000
- .saturating_add((156_000 as Weight).saturating_mul(e as Weight))
- }
- fn instr_call(r: u32) -> Weight {
- (24_599_000 as Weight)
- // Standard Error: 102_000
- .saturating_add((95_771_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_call_indirect(r: u32) -> Weight {
- (32_584_000 as Weight)
- // Standard Error: 176_000
- .saturating_add((193_216_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_call_indirect_per_param(p: u32) -> Weight {
- (240_739_000 as Weight)
- // Standard Error: 6_000
- .saturating_add((3_407_000 as Weight).saturating_mul(p as Weight))
- }
- fn instr_local_get(r: u32) -> Weight {
- (41_963_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((3_110_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_local_set(r: u32) -> Weight {
- (41_956_000 as Weight)
- // Standard Error: 9_000
- .saturating_add((3_460_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_local_tee(r: u32) -> Weight {
- (42_002_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((4_591_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_global_get(r: u32) -> Weight {
- (27_646_000 as Weight)
- // Standard Error: 23_000
- .saturating_add((7_821_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_global_set(r: u32) -> Weight {
- (27_615_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((11_807_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_memory_current(r: u32) -> Weight {
- (27_106_000 as Weight)
- // Standard Error: 78_000
- .saturating_add((2_952_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_memory_grow(r: u32) -> Weight {
- (24_956_000 as Weight)
- // Standard Error: 3_541_000
- .saturating_add((2_332_414_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64clz(r: u32) -> Weight {
- (24_183_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((5_166_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ctz(r: u32) -> Weight {
- (24_142_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((5_146_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64popcnt(r: u32) -> Weight {
- (24_161_000 as Weight)
- // Standard Error: 23_000
- .saturating_add((5_807_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64eqz(r: u32) -> Weight {
- (24_167_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((5_288_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64extendsi32(r: u32) -> Weight {
- (24_252_000 as Weight)
- // Standard Error: 9_000
- .saturating_add((5_091_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64extendui32(r: u32) -> Weight {
- (24_243_000 as Weight)
- // Standard Error: 16_000
- .saturating_add((5_076_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i32wrapi64(r: u32) -> Weight {
- (24_227_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((5_135_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64eq(r: u32) -> Weight {
- (24_278_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((7_124_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ne(r: u32) -> Weight {
- (24_254_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_067_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64lts(r: u32) -> Weight {
- (24_220_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((7_122_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ltu(r: u32) -> Weight {
- (24_221_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_221_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64gts(r: u32) -> Weight {
- (24_259_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_135_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64gtu(r: u32) -> Weight {
- (24_245_000 as Weight)
- // Standard Error: 10_000
- .saturating_add((7_193_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64les(r: u32) -> Weight {
- (24_289_000 as Weight)
- // Standard Error: 22_000
- .saturating_add((7_023_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64leu(r: u32) -> Weight {
- (24_239_000 as Weight)
- // Standard Error: 21_000
- .saturating_add((7_065_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64ges(r: u32) -> Weight {
- (24_256_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_119_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64geu(r: u32) -> Weight {
- (24_240_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((7_225_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64add(r: u32) -> Weight {
- (24_266_000 as Weight)
- // Standard Error: 24_000
- .saturating_add((6_996_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64sub(r: u32) -> Weight {
- (24_265_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((6_974_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64mul(r: u32) -> Weight {
- (24_232_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((7_103_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64divs(r: u32) -> Weight {
- (24_245_000 as Weight)
- // Standard Error: 20_000
- .saturating_add((12_915_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64divu(r: u32) -> Weight {
- (24_177_000 as Weight)
- // Standard Error: 21_000
- .saturating_add((12_232_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rems(r: u32) -> Weight {
- (24_171_000 as Weight)
- // Standard Error: 15_000
- .saturating_add((12_939_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64remu(r: u32) -> Weight {
- (24_788_000 as Weight)
- // Standard Error: 22_000
- .saturating_add((11_657_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64and(r: u32) -> Weight {
- (24_252_000 as Weight)
- // Standard Error: 19_000
- .saturating_add((7_003_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64or(r: u32) -> Weight {
- (24_263_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((7_005_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64xor(r: u32) -> Weight {
- (24_239_000 as Weight)
- // Standard Error: 17_000
- .saturating_add((7_020_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shl(r: u32) -> Weight {
- (24_212_000 as Weight)
- // Standard Error: 13_000
- .saturating_add((7_172_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shrs(r: u32) -> Weight {
- (24_220_000 as Weight)
- // Standard Error: 27_000
- .saturating_add((7_246_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64shru(r: u32) -> Weight {
- (24_213_000 as Weight)
- // Standard Error: 14_000
- .saturating_add((7_191_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rotl(r: u32) -> Weight {
- (24_221_000 as Weight)
- // Standard Error: 18_000
- .saturating_add((7_192_000 as Weight).saturating_mul(r as Weight))
- }
- fn instr_i64rotr(r: u32) -> Weight {
- (24_235_000 as Weight)
- // Standard Error: 12_000
- .saturating_add((7_106_000 as Weight).saturating_mul(r as Weight))
- }
-}