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.mddiffbeforeafterboth--- a/pallets/contracts/rpc/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Node-specific RPC methods for interaction with contracts.
-
-License: Apache-2.0
\ No newline at end of file
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.rsdiffbeforeafterboth1// This file is part of Substrate.23// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.4// SPDX-License-Identifier: Apache-2.056// Licensed under the Apache License, Version 2.0 (the "License");7// you may not use this file except in compliance with the License.8// You may obtain a copy of the License at9//10// http://www.apache.org/licenses/LICENSE-2.011//12// Unless required by applicable law or agreed to in writing, software13// distributed under the License is distributed on an "AS IS" BASIS,14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15// See the License for the specific language governing permissions and16// limitations under the License.1718use crate::{19 BalanceOf, ContractInfo, ContractInfoOf, Pallet, Config, Schedule, Error,20 storage::Storage,21 chain_extension::{22 Result as ExtensionResult, Environment, ChainExtension, Ext, SysConfig, RetVal,23 UncheckedFrom, InitState, ReturnFlags,24 },25 exec::{AccountIdOf, Executable},26 wasm::PrefabWasmModule,27 weights::WeightInfo,28 wasm::ReturnCode as RuntimeReturnCode,29 storage::RawAliveContractInfo,30};31use assert_matches::assert_matches;32use codec::Encode;33use sp_core::Bytes;34use sp_runtime::{35 traits::{BlakeTwo256, Hash, IdentityLookup, Convert},36 testing::{Header, H256},37 AccountId32, Perbill,38};39use sp_io::hashing::blake2_256;40use frame_support::{41 assert_ok, assert_err, assert_err_ignore_postinfo, parameter_types, assert_storage_noop,42 traits::{Currency, ReservableCurrency, OnInitialize, GenesisBuild},43 weights::{Weight, PostDispatchInfo, DispatchClass, constants::WEIGHT_PER_SECOND},44 dispatch::DispatchErrorWithPostInfo,45 storage::child,46};47use frame_system::{self as system, EventRecord, Phase};48use pretty_assertions::assert_eq;4950use crate as pallet_contracts;5152type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;53type Block = frame_system::mocking::MockBlock<Test>;5455frame_support::construct_runtime!(56 pub enum Test where57 Block = Block,58 NodeBlock = Block,59 UncheckedExtrinsic = UncheckedExtrinsic,60 {61 System: frame_system::{Pallet, Call, Config, Storage, Event<T>},62 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},63 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},64 Randomness: pallet_randomness_collective_flip::{Pallet, Call, Storage},65 Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},66 }67);6869#[macro_use]70pub mod test_utils {71 use super::{Test, Balances};72 use crate::{73 ContractInfoOf, CodeHash,74 storage::Storage,75 exec::{StorageKey, AccountIdOf},76 Pallet as Contracts,77 };78 use frame_support::traits::Currency;7980 pub fn set_storage(addr: &AccountIdOf<Test>, key: &StorageKey, value: Option<Vec<u8>>) {81 let contract_info = <ContractInfoOf<Test>>::get(&addr)82 .unwrap()83 .get_alive()84 .unwrap();85 Storage::<Test>::write(addr, &contract_info.trie_id, key, value).unwrap();86 }87 pub fn get_storage(addr: &AccountIdOf<Test>, key: &StorageKey) -> Option<Vec<u8>> {88 let contract_info = <ContractInfoOf<Test>>::get(&addr)89 .unwrap()90 .get_alive()91 .unwrap();92 Storage::<Test>::read(&contract_info.trie_id, key)93 }94 pub fn place_contract(address: &AccountIdOf<Test>, code_hash: CodeHash<Test>) {95 let trie_id = Storage::<Test>::generate_trie_id(address);96 set_balance(address, Contracts::<Test>::subsistence_threshold() * 10);97 Storage::<Test>::place_contract(&address, trie_id, code_hash).unwrap();98 }99 pub fn set_balance(who: &AccountIdOf<Test>, amount: u64) {100 let imbalance = Balances::deposit_creating(who, amount);101 drop(imbalance);102 }103 pub fn get_balance(who: &AccountIdOf<Test>) -> u64 {104 Balances::free_balance(who)105 }106 macro_rules! assert_return_code {107 ( $x:expr , $y:expr $(,)? ) => {{108 use sp_std::convert::TryInto;109 assert_eq!(110 u32::from_le_bytes($x.data[..].try_into().unwrap()),111 $y as u32112 );113 }};114 }115 macro_rules! assert_refcount {116 ( $code_hash:expr , $should:expr $(,)? ) => {{117 let is = crate::CodeStorage::<Test>::get($code_hash)118 .map(|m| m.refcount())119 .unwrap_or(0);120 assert_eq!(is, $should);121 }};122 }123}124125thread_local! {126 static TEST_EXTENSION: sp_std::cell::RefCell<TestExtension> = Default::default();127}128129pub struct TestExtension {130 enabled: bool,131 last_seen_buffer: Vec<u8>,132 last_seen_inputs: (u32, u32, u32, u32),133}134135impl TestExtension {136 fn disable() {137 TEST_EXTENSION.with(|e| e.borrow_mut().enabled = false)138 }139140 fn last_seen_buffer() -> Vec<u8> {141 TEST_EXTENSION.with(|e| e.borrow().last_seen_buffer.clone())142 }143144 fn last_seen_inputs() -> (u32, u32, u32, u32) {145 TEST_EXTENSION.with(|e| e.borrow().last_seen_inputs.clone())146 }147}148149impl Default for TestExtension {150 fn default() -> Self {151 Self {152 enabled: true,153 last_seen_buffer: vec![],154 last_seen_inputs: (0, 0, 0, 0),155 }156 }157}158159impl ChainExtension<Test> for TestExtension {160 fn call<E>(func_id: u32, env: Environment<E, InitState>) -> ExtensionResult<RetVal>161 where162 E: Ext<T = Test>,163 <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,164 {165 match func_id {166 0 => {167 let mut env = env.buf_in_buf_out();168 let input = env.read(2)?;169 env.write(&input, false, None)?;170 TEST_EXTENSION.with(|e| e.borrow_mut().last_seen_buffer = input);171 Ok(RetVal::Converging(func_id))172 }173 1 => {174 let env = env.only_in();175 TEST_EXTENSION.with(|e| {176 e.borrow_mut().last_seen_inputs =177 (env.val0(), env.val1(), env.val2(), env.val3())178 });179 Ok(RetVal::Converging(func_id))180 }181 2 => {182 let mut env = env.buf_in_buf_out();183 let weight = env.read(2)?[1].into();184 env.charge_weight(weight)?;185 Ok(RetVal::Converging(func_id))186 }187 3 => Ok(RetVal::Diverging {188 flags: ReturnFlags::REVERT,189 data: vec![42, 99],190 }),191 _ => {192 panic!(193 "Passed unknown func_id to test chain extension: {}",194 func_id195 );196 }197 }198 }199200 fn enabled() -> bool {201 TEST_EXTENSION.with(|e| e.borrow().enabled)202 }203}204205parameter_types! {206 pub const BlockHashCount: u64 = 250;207 pub BlockWeights: frame_system::limits::BlockWeights =208 frame_system::limits::BlockWeights::simple_max(2 * WEIGHT_PER_SECOND);209 pub static ExistentialDeposit: u64 = 0;210}211impl frame_system::Config for Test {212 type BaseCallFilter = ();213 type BlockWeights = BlockWeights;214 type BlockLength = ();215 type DbWeight = ();216 type Origin = Origin;217 type Index = u64;218 type BlockNumber = u64;219 type Hash = H256;220 type Call = Call;221 type Hashing = BlakeTwo256;222 type AccountId = AccountId32;223 type Lookup = IdentityLookup<Self::AccountId>;224 type Header = Header;225 type Event = Event;226 type BlockHashCount = BlockHashCount;227 type Version = ();228 type PalletInfo = PalletInfo;229 type AccountData = pallet_balances::AccountData<u64>;230 type OnNewAccount = ();231 type OnKilledAccount = ();232 type SystemWeightInfo = ();233 type SS58Prefix = ();234 type OnSetCode = ();235}236impl pallet_balances::Config for Test {237 type MaxLocks = ();238 type Balance = u64;239 type Event = Event;240 type DustRemoval = ();241 type ExistentialDeposit = ExistentialDeposit;242 type AccountStore = System;243 type WeightInfo = ();244}245parameter_types! {246 pub const MinimumPeriod: u64 = 1;247}248impl pallet_timestamp::Config for Test {249 type Moment = u64;250 type OnTimestampSet = ();251 type MinimumPeriod = MinimumPeriod;252 type WeightInfo = ();253}254parameter_types! {255 pub const SignedClaimHandicap: u64 = 2;256 pub const TombstoneDeposit: u64 = 16;257 pub const DepositPerContract: u64 = 8 * DepositPerStorageByte::get();258 pub const DepositPerStorageByte: u64 = 10_000;259 pub const DepositPerStorageItem: u64 = 10_000;260 pub RentFraction: Perbill = Perbill::from_rational(4u32, 10_000u32);261 pub const SurchargeReward: u64 = 500_000;262 pub const MaxDepth: u32 = 100;263 pub const MaxValueSize: u32 = 16_384;264 pub const DeletionQueueDepth: u32 = 1024;265 pub const DeletionWeightLimit: Weight = 500_000_000_000;266 pub const MaxCodeSize: u32 = 2 * 1024;267}268269parameter_types! {270 pub const TransactionByteFee: u64 = 0;271}272273impl Convert<Weight, BalanceOf<Self>> for Test {274 fn convert(w: Weight) -> BalanceOf<Self> {275 w276 }277}278279impl Config for Test {280 type Time = Timestamp;281 type Randomness = Randomness;282 type Currency = Balances;283 type Event = Event;284 type RentPayment = ();285 type SignedClaimHandicap = SignedClaimHandicap;286 type TombstoneDeposit = TombstoneDeposit;287 type DepositPerContract = DepositPerContract;288 type DepositPerStorageByte = DepositPerStorageByte;289 type DepositPerStorageItem = DepositPerStorageItem;290 type RentFraction = RentFraction;291 type SurchargeReward = SurchargeReward;292 type MaxDepth = MaxDepth;293 type MaxValueSize = MaxValueSize;294 type WeightPrice = Self;295 type WeightInfo = ();296 type ChainExtension = TestExtension;297 type DeletionQueueDepth = DeletionQueueDepth;298 type DeletionWeightLimit = DeletionWeightLimit;299 type MaxCodeSize = MaxCodeSize;300}301302pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);303pub const BOB: AccountId32 = AccountId32::new([2u8; 32]);304pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]);305pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]);306307const GAS_LIMIT: Weight = 10_000_000_000;308309pub struct ExtBuilder {310 existential_deposit: u64,311}312impl Default for ExtBuilder {313 fn default() -> Self {314 Self {315 existential_deposit: 1,316 }317 }318}319impl ExtBuilder {320 pub fn existential_deposit(mut self, existential_deposit: u64) -> Self {321 self.existential_deposit = existential_deposit;322 self323 }324 pub fn set_associated_consts(&self) {325 EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);326 }327 pub fn build(self) -> sp_io::TestExternalities {328 self.set_associated_consts();329 let mut t = frame_system::GenesisConfig::default()330 .build_storage::<Test>()331 .unwrap();332 pallet_balances::GenesisConfig::<Test> { balances: vec![] }333 .assimilate_storage(&mut t)334 .unwrap();335 pallet_contracts::GenesisConfig {336 current_schedule: Schedule::<Test> {337 enable_println: true,338 ..Default::default()339 },340 }341 .assimilate_storage(&mut t)342 .unwrap();343 let mut ext = sp_io::TestExternalities::new(t);344 ext.execute_with(|| System::set_block_number(1));345 ext346 }347}348349/// Load a given wasm module represented by a .wat file and returns a wasm binary contents along350/// with it's hash.351///352/// The fixture files are located under the `fixtures/` directory.353fn compile_module<T>(fixture_name: &str) -> wat::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>354where355 T: frame_system::Config,356{357 let fixture_path = ["fixtures/", fixture_name, ".wat"].concat();358 let wasm_binary = wat::parse_file(fixture_path)?;359 let code_hash = T::Hashing::hash(&wasm_binary);360 Ok((wasm_binary, code_hash))361}362363// Perform a call to a plain account.364// The actual transfer fails because we can only call contracts.365// Then we check that at least the base costs where charged (no runtime gas costs.)366#[test]367fn calling_plain_account_fails() {368 ExtBuilder::default().build().execute_with(|| {369 let _ = Balances::deposit_creating(&ALICE, 100_000_000);370 let base_cost = <<Test as Config>::WeightInfo as WeightInfo>::call(0);371372 assert_eq!(373 Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, Vec::new()),374 Err(DispatchErrorWithPostInfo {375 error: Error::<Test>::NotCallable.into(),376 post_info: PostDispatchInfo {377 actual_weight: Some(base_cost),378 pays_fee: Default::default(),379 },380 })381 );382 });383}384385#[test]386fn account_removal_does_not_remove_storage() {387 use self::test_utils::{set_storage, get_storage};388389 ExtBuilder::default()390 .existential_deposit(100)391 .build()392 .execute_with(|| {393 let trie_id1 = Storage::<Test>::generate_trie_id(&ALICE);394 let trie_id2 = Storage::<Test>::generate_trie_id(&BOB);395 let key1 = &[1; 32];396 let key2 = &[2; 32];397398 // Set up two accounts with free balance above the existential threshold.399 {400 let alice_contract_info = ContractInfo::Alive(RawAliveContractInfo {401 trie_id: trie_id1.clone(),402 storage_size: 0,403 pair_count: 0,404 deduct_block: System::block_number(),405 code_hash: H256::repeat_byte(1),406 rent_allowance: 40,407 rent_payed: 0,408 last_write: None,409 _reserved: None,410 });411 let _ = Balances::deposit_creating(&ALICE, 110);412 ContractInfoOf::<Test>::insert(ALICE, &alice_contract_info);413 set_storage(&ALICE, &key1, Some(b"1".to_vec()));414 set_storage(&ALICE, &key2, Some(b"2".to_vec()));415416 let bob_contract_info = ContractInfo::Alive(RawAliveContractInfo {417 trie_id: trie_id2.clone(),418 storage_size: 0,419 pair_count: 0,420 deduct_block: System::block_number(),421 code_hash: H256::repeat_byte(2),422 rent_allowance: 40,423 rent_payed: 0,424 last_write: None,425 _reserved: None,426 });427 let _ = Balances::deposit_creating(&BOB, 110);428 ContractInfoOf::<Test>::insert(BOB, &bob_contract_info);429 set_storage(&BOB, &key1, Some(b"3".to_vec()));430 set_storage(&BOB, &key2, Some(b"4".to_vec()));431 }432433 // Transfer funds from ALICE account of such amount that after this transfer434 // the balance of the ALICE account will be below the existential threshold.435 //436 // This does not remove the contract storage as we are not notified about a437 // account removal. This cannot happen in reality because a contract can only438 // remove itself by `seal_terminate`. There is no external event that can remove439 // the account appart from that.440 assert_ok!(Balances::transfer(Origin::signed(ALICE), BOB, 20));441442 // Verify that no entries are removed.443 {444 assert_eq!(get_storage(&ALICE, key1), Some(b"1".to_vec()));445 assert_eq!(get_storage(&ALICE, key2), Some(b"2".to_vec()));446447 assert_eq!(get_storage(&BOB, key1), Some(b"3".to_vec()));448 assert_eq!(get_storage(&BOB, key2), Some(b"4".to_vec()));449 }450 });451}452453#[test]454fn instantiate_and_call_and_deposit_event() {455 let (wasm, code_hash) = compile_module::<Test>("return_from_start_fn").unwrap();456457 ExtBuilder::default()458 .existential_deposit(100)459 .build()460 .execute_with(|| {461 let _ = Balances::deposit_creating(&ALICE, 1_000_000);462 let subsistence = Pallet::<Test>::subsistence_threshold();463464 // Check at the end to get hash on error easily465 let creation = Contracts::instantiate_with_code(466 Origin::signed(ALICE),467 subsistence * 100,468 GAS_LIMIT,469 wasm,470 vec![],471 vec![],472 );473 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);474475 assert_eq!(476 System::events(),477 vec![478 EventRecord {479 phase: Phase::Initialization,480 event: Event::frame_system(frame_system::Event::NewAccount(ALICE.clone())),481 topics: vec![],482 },483 EventRecord {484 phase: Phase::Initialization,485 event: Event::pallet_balances(pallet_balances::Event::Endowed(486 ALICE, 1_000_000487 )),488 topics: vec![],489 },490 EventRecord {491 phase: Phase::Initialization,492 event: Event::frame_system(frame_system::Event::NewAccount(addr.clone())),493 topics: vec![],494 },495 EventRecord {496 phase: Phase::Initialization,497 event: Event::pallet_balances(pallet_balances::Event::Endowed(498 addr.clone(),499 subsistence * 100500 )),501 topics: vec![],502 },503 EventRecord {504 phase: Phase::Initialization,505 event: Event::pallet_balances(pallet_balances::Event::Transfer(506 ALICE,507 addr.clone(),508 subsistence * 100509 )),510 topics: vec![],511 },512 EventRecord {513 phase: Phase::Initialization,514 event: Event::pallet_contracts(crate::Event::CodeStored(code_hash.into())),515 topics: vec![],516 },517 EventRecord {518 phase: Phase::Initialization,519 event: Event::pallet_contracts(crate::Event::ContractEmitted(520 addr.clone(),521 vec![1, 2, 3, 4]522 )),523 topics: vec![],524 },525 EventRecord {526 phase: Phase::Initialization,527 event: Event::pallet_contracts(crate::Event::Instantiated(528 ALICE,529 addr.clone()530 )),531 topics: vec![],532 },533 ]534 );535536 assert_ok!(creation);537 assert!(ContractInfoOf::<Test>::contains_key(&addr));538 });539}540541#[test]542fn deposit_event_max_value_limit() {543 let (wasm, code_hash) = compile_module::<Test>("event_size").unwrap();544545 ExtBuilder::default()546 .existential_deposit(50)547 .build()548 .execute_with(|| {549 // Create550 let _ = Balances::deposit_creating(&ALICE, 1_000_000);551 assert_ok!(Contracts::instantiate_with_code(552 Origin::signed(ALICE),553 30_000,554 GAS_LIMIT,555 wasm,556 vec![],557 vec![],558 ));559 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);560561 // Call contract with allowed storage value.562 assert_ok!(Contracts::call(563 Origin::signed(ALICE),564 addr.clone(),565 0,566 GAS_LIMIT * 2, // we are copying a huge buffer,567 <Test as Config>::MaxValueSize::get().encode(),568 ));569570 // Call contract with too large a storage value.571 assert_err_ignore_postinfo!(572 Contracts::call(573 Origin::signed(ALICE),574 addr,575 0,576 GAS_LIMIT,577 (<Test as Config>::MaxValueSize::get() + 1).encode(),578 ),579 Error::<Test>::ValueTooLarge,580 );581 });582}583584#[test]585fn run_out_of_gas() {586 let (wasm, code_hash) = compile_module::<Test>("run_out_of_gas").unwrap();587 let subsistence = Pallet::<Test>::subsistence_threshold();588589 ExtBuilder::default()590 .existential_deposit(50)591 .build()592 .execute_with(|| {593 let _ = Balances::deposit_creating(&ALICE, 1_000_000);594595 assert_ok!(Contracts::instantiate_with_code(596 Origin::signed(ALICE),597 100 * subsistence,598 GAS_LIMIT,599 wasm,600 vec![],601 vec![],602 ));603 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);604605 // Call the contract with a fixed gas limit. It must run out of gas because it just606 // loops forever.607 assert_err_ignore_postinfo!(608 Contracts::call(609 Origin::signed(ALICE),610 addr, // newly created account611 0,612 67_500_000,613 vec![],614 ),615 Error::<Test>::OutOfGas,616 );617 });618}619620/// Input data for each call in set_rent code621mod call {622 use super::{AccountIdOf, Test};623 pub fn set_storage_4_byte() -> Vec<u8> {624 0u32.to_le_bytes().to_vec()625 }626 pub fn remove_storage_4_byte() -> Vec<u8> {627 1u32.to_le_bytes().to_vec()628 }629 #[allow(dead_code)]630 pub fn transfer(to: &AccountIdOf<Test>) -> Vec<u8> {631 2u32.to_le_bytes()632 .iter()633 .chain(AsRef::<[u8]>::as_ref(to))634 .cloned()635 .collect()636 }637 pub fn null() -> Vec<u8> {638 3u32.to_le_bytes().to_vec()639 }640}641642#[test]643fn storage_size() {644 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();645646 // Storage size647 ExtBuilder::default()648 .existential_deposit(50)649 .build()650 .execute_with(|| {651 // Create652 let _ = Balances::deposit_creating(&ALICE, 1_000_000);653 assert_ok!(Contracts::instantiate_with_code(654 Origin::signed(ALICE),655 30_000,656 GAS_LIMIT,657 wasm,658 // rent_allowance659 <Test as pallet_balances::Config>::Balance::from(10_000u32).encode(),660 vec![],661 ));662 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);663 let bob_contract = ContractInfoOf::<Test>::get(&addr)664 .unwrap()665 .get_alive()666 .unwrap();667 assert_eq!(bob_contract.storage_size, 4);668 assert_eq!(bob_contract.pair_count, 1,);669670 assert_ok!(Contracts::call(671 Origin::signed(ALICE),672 addr.clone(),673 0,674 GAS_LIMIT,675 call::set_storage_4_byte()676 ));677 let bob_contract = ContractInfoOf::<Test>::get(&addr)678 .unwrap()679 .get_alive()680 .unwrap();681 assert_eq!(bob_contract.storage_size, 4 + 4);682 assert_eq!(bob_contract.pair_count, 2,);683684 assert_ok!(Contracts::call(685 Origin::signed(ALICE),686 addr.clone(),687 0,688 GAS_LIMIT,689 call::remove_storage_4_byte()690 ));691 let bob_contract = ContractInfoOf::<Test>::get(&addr)692 .unwrap()693 .get_alive()694 .unwrap();695 assert_eq!(bob_contract.storage_size, 4);696 assert_eq!(bob_contract.pair_count, 1,);697 });698}699700#[test]701fn empty_kv_pairs() {702 let (wasm, code_hash) = compile_module::<Test>("set_empty_storage").unwrap();703704 ExtBuilder::default().build().execute_with(|| {705 let _ = Balances::deposit_creating(&ALICE, 1_000_000);706 assert_ok!(Contracts::instantiate_with_code(707 Origin::signed(ALICE),708 30_000,709 GAS_LIMIT,710 wasm,711 vec![],712 vec![],713 ));714 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);715 let bob_contract = ContractInfoOf::<Test>::get(&addr)716 .unwrap()717 .get_alive()718 .unwrap();719720 assert_eq!(bob_contract.storage_size, 0,);721 assert_eq!(bob_contract.pair_count, 1,);722 });723}724725fn initialize_block(number: u64) {726 System::initialize(727 &number,728 &[0u8; 32].into(),729 &Default::default(),730 Default::default(),731 );732}733734#[test]735fn deduct_blocks() {736 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();737 let endowment: BalanceOf<Test> = 100_000;738 let allowance: BalanceOf<Test> = 70_000;739740 ExtBuilder::default()741 .existential_deposit(50)742 .build()743 .execute_with(|| {744 // Create745 let _ = Balances::deposit_creating(&ALICE, 1_000_000);746 assert_ok!(Contracts::instantiate_with_code(747 Origin::signed(ALICE),748 endowment,749 GAS_LIMIT,750 wasm,751 allowance.encode(),752 vec![],753 ));754 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);755 let contract = ContractInfoOf::<Test>::get(&addr)756 .unwrap()757 .get_alive()758 .unwrap();759 let code_len: BalanceOf<Test> =760 PrefabWasmModule::<Test>::from_storage_noinstr(contract.code_hash)761 .unwrap()762 .occupied_storage()763 .into();764765 // The instantiation deducted the rent for one block immediately766 let rent0 = <Test as Config>::RentFraction::get()767 // (base_deposit(8) + bytes in storage(4) + size of code) * byte_price768 // + 1 storage item (10_000) - free_balance769 .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - endowment)770 // blocks to rent771 * 1;772 assert!(rent0 > 0);773 assert_eq!(contract.rent_allowance, allowance - rent0);774 assert_eq!(contract.deduct_block, 1);775 assert_eq!(Balances::free_balance(&addr), endowment - rent0);776777 // Advance 4 blocks778 initialize_block(5);779780 // Trigger rent through call781 assert_ok!(Contracts::call(782 Origin::signed(ALICE),783 addr.clone(),784 0,785 GAS_LIMIT,786 call::null()787 ));788789 // Check result790 let rent = <Test as Config>::RentFraction::get()791 .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0))792 * 4;793 let contract = ContractInfoOf::<Test>::get(&addr)794 .unwrap()795 .get_alive()796 .unwrap();797 assert_eq!(contract.rent_allowance, allowance - rent0 - rent);798 assert_eq!(contract.deduct_block, 5);799 assert_eq!(Balances::free_balance(&addr), endowment - rent0 - rent);800801 // Advance 2 blocks more802 initialize_block(7);803804 // Trigger rent through call805 assert_ok!(Contracts::call(806 Origin::signed(ALICE),807 addr.clone(),808 0,809 GAS_LIMIT,810 call::null()811 ));812813 // Check result814 let rent_2 = <Test as Config>::RentFraction::get()815 .mul_ceil((8 + 4 + code_len) * 10_000 + 10_000 - (endowment - rent0 - rent))816 * 2;817 let contract = ContractInfoOf::<Test>::get(&addr)818 .unwrap()819 .get_alive()820 .unwrap();821 assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2);822 assert_eq!(contract.deduct_block, 7);823 assert_eq!(824 Balances::free_balance(&addr),825 endowment - rent0 - rent - rent_2826 );827828 // Second call on same block should have no effect on rent829 assert_ok!(Contracts::call(830 Origin::signed(ALICE),831 addr.clone(),832 0,833 GAS_LIMIT,834 call::null()835 ));836 let contract = ContractInfoOf::<Test>::get(&addr)837 .unwrap()838 .get_alive()839 .unwrap();840 assert_eq!(contract.rent_allowance, allowance - rent0 - rent - rent_2);841 assert_eq!(contract.deduct_block, 7);842 assert_eq!(843 Balances::free_balance(&addr),844 endowment - rent0 - rent - rent_2845 )846 });847}848849#[test]850fn inherent_claim_surcharge_contract_removals() {851 removals(|addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok());852}853854#[test]855fn signed_claim_surcharge_contract_removals() {856 removals(|addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok());857}858859#[test]860fn claim_surcharge_malus() {861 // Test surcharge malus for inherent862 claim_surcharge(863 8,864 |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),865 true,866 );867 claim_surcharge(868 7,869 |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),870 true,871 );872 claim_surcharge(873 6,874 |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),875 true,876 );877 claim_surcharge(878 5,879 |addr| Contracts::claim_surcharge(Origin::none(), addr, Some(ALICE)).is_ok(),880 false,881 );882883 // Test surcharge malus for signed884 claim_surcharge(885 8,886 |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),887 true,888 );889 claim_surcharge(890 7,891 |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),892 false,893 );894 claim_surcharge(895 6,896 |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),897 false,898 );899 claim_surcharge(900 5,901 |addr| Contracts::claim_surcharge(Origin::signed(ALICE), addr, None).is_ok(),902 false,903 );904}905906/// Claim surcharge with the given trigger_call at the given blocks.907/// If `removes` is true then assert that the contract is a tombstone.908fn claim_surcharge(blocks: u64, trigger_call: impl Fn(AccountIdOf<Test>) -> bool, removes: bool) {909 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();910911 ExtBuilder::default()912 .existential_deposit(50)913 .build()914 .execute_with(|| {915 // Create916 let _ = Balances::deposit_creating(&ALICE, 1_000_000);917 assert_ok!(Contracts::instantiate_with_code(918 Origin::signed(ALICE),919 100_000,920 GAS_LIMIT,921 wasm,922 <Test as pallet_balances::Config>::Balance::from(30_000u32).encode(), // rent allowance923 vec![],924 ));925 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);926927 // Advance blocks928 initialize_block(blocks);929930 // Trigger rent through call931 assert_eq!(trigger_call(addr.clone()), removes);932933 if removes {934 assert!(ContractInfoOf::<Test>::get(&addr)935 .unwrap()936 .get_tombstone()937 .is_some());938 } else {939 assert!(ContractInfoOf::<Test>::get(&addr)940 .unwrap()941 .get_alive()942 .is_some());943 }944 });945}946947/// Test for all kind of removals for the given trigger:948/// * if balance is reached and balance > subsistence threshold949/// * if allowance is exceeded950/// * if balance is reached and balance < subsistence threshold951/// * this case cannot be triggered by a contract: we check whether a tombstone is left952fn removals(trigger_call: impl Fn(AccountIdOf<Test>) -> bool) {953 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();954955 // Balance reached and superior to subsistence threshold956 ExtBuilder::default()957 .existential_deposit(50)958 .build()959 .execute_with(|| {960 // Create961 let _ = Balances::deposit_creating(&ALICE, 1_000_000);962 assert_ok!(Contracts::instantiate_with_code(963 Origin::signed(ALICE),964 70_000,965 GAS_LIMIT,966 wasm.clone(),967 <Test as pallet_balances::Config>::Balance::from(100_000u32).encode(), // rent allowance968 vec![],969 ));970 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);971 let allowance = ContractInfoOf::<Test>::get(&addr)972 .unwrap()973 .get_alive()974 .unwrap()975 .rent_allowance;976 let balance = Balances::free_balance(&addr);977978 let subsistence_threshold = Pallet::<Test>::subsistence_threshold();979980 // Trigger rent must have no effect981 assert!(!trigger_call(addr.clone()));982 assert_eq!(983 ContractInfoOf::<Test>::get(&addr)984 .unwrap()985 .get_alive()986 .unwrap()987 .rent_allowance,988 allowance,989 );990 assert_eq!(Balances::free_balance(&addr), balance);991992 // Advance blocks993 initialize_block(27);994995 // Trigger rent through call (should remove the contract)996 assert!(trigger_call(addr.clone()));997 assert!(ContractInfoOf::<Test>::get(&addr)998 .unwrap()999 .get_tombstone()1000 .is_some());1001 assert_eq!(Balances::free_balance(&addr), subsistence_threshold);10021003 // Advance blocks1004 initialize_block(30);10051006 // Trigger rent must have no effect1007 assert!(!trigger_call(addr.clone()));1008 assert!(ContractInfoOf::<Test>::get(&addr)1009 .unwrap()1010 .get_tombstone()1011 .is_some());1012 assert_eq!(Balances::free_balance(&addr), subsistence_threshold);1013 });10141015 // Allowance exceeded1016 ExtBuilder::default()1017 .existential_deposit(50)1018 .build()1019 .execute_with(|| {1020 // Create1021 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1022 assert_ok!(Contracts::instantiate_with_code(1023 Origin::signed(ALICE),1024 100_000,1025 GAS_LIMIT,1026 wasm.clone(),1027 <Test as pallet_balances::Config>::Balance::from(70_000u32).encode(), // rent allowance1028 vec![],1029 ));1030 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);1031 let allowance = ContractInfoOf::<Test>::get(&addr)1032 .unwrap()1033 .get_alive()1034 .unwrap()1035 .rent_allowance;1036 let balance = Balances::free_balance(&addr);10371038 // Trigger rent must have no effect1039 assert!(!trigger_call(addr.clone()));1040 assert_eq!(1041 ContractInfoOf::<Test>::get(&addr)1042 .unwrap()1043 .get_alive()1044 .unwrap()1045 .rent_allowance,1046 allowance,1047 );1048 assert_eq!(Balances::free_balance(&addr), balance);10491050 // Advance blocks1051 initialize_block(27);10521053 // Trigger rent through call1054 assert!(trigger_call(addr.clone()));1055 assert!(ContractInfoOf::<Test>::get(&addr)1056 .unwrap()1057 .get_tombstone()1058 .is_some());1059 // Balance should be initial balance - initial rent_allowance1060 assert_eq!(Balances::free_balance(&addr), 30_000);10611062 // Advance blocks1063 initialize_block(20);10641065 // Trigger rent must have no effect1066 assert!(!trigger_call(addr.clone()));1067 assert!(ContractInfoOf::<Test>::get(&addr)1068 .unwrap()1069 .get_tombstone()1070 .is_some());1071 assert_eq!(Balances::free_balance(&addr), 30_000);1072 });10731074 // Balance reached and inferior to subsistence threshold1075 ExtBuilder::default()1076 .existential_deposit(50)1077 .build()1078 .execute_with(|| {1079 // Create1080 let subsistence_threshold = Pallet::<Test>::subsistence_threshold();1081 let _ = Balances::deposit_creating(&ALICE, subsistence_threshold * 1000);1082 assert_ok!(Contracts::instantiate_with_code(1083 Origin::signed(ALICE),1084 subsistence_threshold * 100,1085 GAS_LIMIT,1086 wasm,1087 (subsistence_threshold * 100).encode(), // rent allowance1088 vec![],1089 ));1090 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);1091 let allowance = ContractInfoOf::<Test>::get(&addr)1092 .unwrap()1093 .get_alive()1094 .unwrap()1095 .rent_allowance;1096 let balance = Balances::free_balance(&addr);10971098 // Trigger rent must have no effect1099 assert!(!trigger_call(addr.clone()));1100 assert_eq!(1101 ContractInfoOf::<Test>::get(&addr)1102 .unwrap()1103 .get_alive()1104 .unwrap()1105 .rent_allowance,1106 allowance,1107 );1108 assert_eq!(Balances::free_balance(&addr), balance,);11091110 // Make contract have exactly the subsistence threshold1111 Balances::make_free_balance_be(&addr, subsistence_threshold);1112 assert_eq!(Balances::free_balance(&addr), subsistence_threshold);11131114 // Advance blocks (should remove as balance is exactly subsistence)1115 initialize_block(10);11161117 // Trigger rent through call1118 assert!(trigger_call(addr.clone()));1119 assert_matches!(1120 ContractInfoOf::<Test>::get(&addr),1121 Some(ContractInfo::Tombstone(_))1122 );1123 assert_eq!(Balances::free_balance(&addr), subsistence_threshold);11241125 // Advance blocks1126 initialize_block(20);11271128 // Trigger rent must have no effect1129 assert!(!trigger_call(addr.clone()));1130 assert_matches!(1131 ContractInfoOf::<Test>::get(&addr),1132 Some(ContractInfo::Tombstone(_))1133 );1134 assert_eq!(Balances::free_balance(&addr), subsistence_threshold);1135 });1136}11371138#[test]1139fn call_removed_contract() {1140 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();11411142 // Balance reached and superior to subsistence threshold1143 ExtBuilder::default()1144 .existential_deposit(50)1145 .build()1146 .execute_with(|| {1147 // Create1148 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1149 assert_ok!(Contracts::instantiate_with_code(1150 Origin::signed(ALICE),1151 30_000,1152 GAS_LIMIT,1153 wasm,1154 // rent allowance1155 <Test as pallet_balances::Config>::Balance::from(10_000u32).encode(),1156 vec![],1157 ));1158 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);11591160 // Calling contract should succeed.1161 assert_ok!(Contracts::call(1162 Origin::signed(ALICE),1163 addr.clone(),1164 0,1165 GAS_LIMIT,1166 call::null()1167 ));11681169 // Advance blocks1170 initialize_block(27);11711172 // Calling contract should deny access because rent cannot be paid.1173 assert_err_ignore_postinfo!(1174 Contracts::call(1175 Origin::signed(ALICE),1176 addr.clone(),1177 0,1178 GAS_LIMIT,1179 call::null()1180 ),1181 Error::<Test>::NotCallable1182 );1183 // No event is generated because the contract is not actually removed.1184 assert_eq!(System::events(), vec![]);11851186 // Subsequent contract calls should also fail.1187 assert_err_ignore_postinfo!(1188 Contracts::call(1189 Origin::signed(ALICE),1190 addr.clone(),1191 0,1192 GAS_LIMIT,1193 call::null()1194 ),1195 Error::<Test>::NotCallable1196 );11971198 // A snitch can now remove the contract1199 assert_ok!(Contracts::claim_surcharge(1200 Origin::none(),1201 addr.clone(),1202 Some(ALICE)1203 ));1204 assert!(ContractInfoOf::<Test>::get(&addr)1205 .unwrap()1206 .get_tombstone()1207 .is_some());1208 })1209}12101211#[test]1212fn default_rent_allowance_on_instantiate() {1213 let (wasm, code_hash) = compile_module::<Test>("check_default_rent_allowance").unwrap();12141215 ExtBuilder::default()1216 .existential_deposit(50)1217 .build()1218 .execute_with(|| {1219 // Create1220 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1221 assert_ok!(Contracts::instantiate_with_code(1222 Origin::signed(ALICE),1223 30_000,1224 GAS_LIMIT,1225 wasm,1226 vec![],1227 vec![],1228 ));1229 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);1230 let contract = ContractInfoOf::<Test>::get(&addr)1231 .unwrap()1232 .get_alive()1233 .unwrap();1234 let code_len: BalanceOf<Test> =1235 PrefabWasmModule::<Test>::from_storage_noinstr(contract.code_hash)1236 .unwrap()1237 .occupied_storage()1238 .into();12391240 // The instantiation deducted the rent for one block immediately1241 let first_rent = <Test as Config>::RentFraction::get()1242 // (base_deposit(8) + code_len) * byte_price - free_balance1243 .mul_ceil((8 + code_len) * 10_000 - 30_000)1244 // blocks to rent1245 * 1;1246 assert_eq!(1247 contract.rent_allowance,1248 <BalanceOf<Test>>::max_value() - first_rent1249 );12501251 // Advance blocks1252 initialize_block(5);12531254 // Trigger rent through call1255 assert_ok!(Contracts::call(1256 Origin::signed(ALICE),1257 addr.clone(),1258 0,1259 GAS_LIMIT,1260 call::null()1261 ));12621263 // Check contract is still alive1264 let contract = ContractInfoOf::<Test>::get(&addr).unwrap().get_alive();1265 assert!(contract.is_some())1266 });1267}12681269#[test]1270fn restorations_dirty_storage_and_different_storage() {1271 restoration(true, true, false);1272}12731274#[test]1275fn restorations_dirty_storage() {1276 restoration(false, true, false);1277}12781279#[test]1280fn restoration_different_storage() {1281 restoration(true, false, false);1282}12831284#[test]1285fn restoration_code_evicted() {1286 restoration(false, false, true);1287}12881289#[test]1290fn restoration_success() {1291 restoration(false, false, false);1292}12931294fn restoration(1295 test_different_storage: bool,1296 test_restore_to_with_dirty_storage: bool,1297 test_code_evicted: bool,1298) {1299 let (set_rent_wasm, set_rent_code_hash) = compile_module::<Test>("set_rent").unwrap();1300 let (restoration_wasm, restoration_code_hash) = compile_module::<Test>("restoration").unwrap();1301 let allowance: <Test as pallet_balances::Config>::Balance = 10_000;13021303 ExtBuilder::default()1304 .existential_deposit(50)1305 .build()1306 .execute_with(|| {1307 let _ = Balances::deposit_creating(&ALICE, 1_000_000);13081309 // Create an account with address `BOB` with code `CODE_SET_RENT`.1310 // The input parameter sets the rent allowance to 0.1311 assert_ok!(Contracts::instantiate_with_code(1312 Origin::signed(ALICE),1313 30_000,1314 GAS_LIMIT,1315 set_rent_wasm.clone(),1316 allowance.encode(),1317 vec![],1318 ));1319 let addr_bob = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[]);13201321 let mut events = vec![1322 EventRecord {1323 phase: Phase::Initialization,1324 event: Event::frame_system(frame_system::Event::NewAccount(ALICE)),1325 topics: vec![],1326 },1327 EventRecord {1328 phase: Phase::Initialization,1329 event: Event::pallet_balances(pallet_balances::Event::Endowed(1330 ALICE, 1_000_000,1331 )),1332 topics: vec![],1333 },1334 EventRecord {1335 phase: Phase::Initialization,1336 event: Event::frame_system(frame_system::Event::NewAccount(addr_bob.clone())),1337 topics: vec![],1338 },1339 EventRecord {1340 phase: Phase::Initialization,1341 event: Event::pallet_balances(pallet_balances::Event::Endowed(1342 addr_bob.clone(),1343 30_000,1344 )),1345 topics: vec![],1346 },1347 EventRecord {1348 phase: Phase::Initialization,1349 event: Event::pallet_balances(pallet_balances::Event::Transfer(1350 ALICE,1351 addr_bob.clone(),1352 30_000,1353 )),1354 topics: vec![],1355 },1356 EventRecord {1357 phase: Phase::Initialization,1358 event: Event::pallet_contracts(crate::Event::CodeStored(1359 set_rent_code_hash.into(),1360 )),1361 topics: vec![],1362 },1363 EventRecord {1364 phase: Phase::Initialization,1365 event: Event::pallet_contracts(crate::Event::Instantiated(1366 ALICE,1367 addr_bob.clone(),1368 )),1369 topics: vec![],1370 },1371 ];13721373 // Create another contract from the same code in order to increment the codes1374 // refcounter so that it stays on chain.1375 if !test_code_evicted {1376 assert_ok!(Contracts::instantiate_with_code(1377 Origin::signed(ALICE),1378 20_000,1379 GAS_LIMIT,1380 set_rent_wasm,1381 allowance.encode(),1382 vec![1],1383 ));1384 assert_refcount!(set_rent_code_hash, 2);1385 let addr_dummy = Contracts::contract_address(&ALICE, &set_rent_code_hash, &[1]);1386 events.extend(1387 [1388 EventRecord {1389 phase: Phase::Initialization,1390 event: Event::frame_system(frame_system::Event::NewAccount(1391 addr_dummy.clone(),1392 )),1393 topics: vec![],1394 },1395 EventRecord {1396 phase: Phase::Initialization,1397 event: Event::pallet_balances(pallet_balances::Event::Endowed(1398 addr_dummy.clone(),1399 20_000,1400 )),1401 topics: vec![],1402 },1403 EventRecord {1404 phase: Phase::Initialization,1405 event: Event::pallet_balances(pallet_balances::Event::Transfer(1406 ALICE,1407 addr_dummy.clone(),1408 20_000,1409 )),1410 topics: vec![],1411 },1412 EventRecord {1413 phase: Phase::Initialization,1414 event: Event::pallet_contracts(crate::Event::Instantiated(1415 ALICE,1416 addr_dummy.clone(),1417 )),1418 topics: vec![],1419 },1420 ]1421 .iter()1422 .cloned(),1423 );1424 }14251426 assert_eq!(System::events(), events);14271428 // Check if `BOB` was created successfully and that the rent allowance is below what1429 // we specified as the first rent was already collected.1430 let bob_contract = ContractInfoOf::<Test>::get(&addr_bob)1431 .unwrap()1432 .get_alive()1433 .unwrap();1434 assert!(bob_contract.rent_allowance < allowance);14351436 if test_different_storage {1437 assert_ok!(Contracts::call(1438 Origin::signed(ALICE),1439 addr_bob.clone(),1440 0,1441 GAS_LIMIT,1442 call::set_storage_4_byte()1443 ));1444 }14451446 // Advance blocks in order to make the contract run out of money for rent.1447 initialize_block(27);14481449 // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 20_0001450 // we expect that it is no longer callable but keeps existing until someone1451 // calls `claim_surcharge`.1452 assert_err_ignore_postinfo!(1453 Contracts::call(1454 Origin::signed(ALICE),1455 addr_bob.clone(),1456 0,1457 GAS_LIMIT,1458 call::null()1459 ),1460 Error::<Test>::NotCallable1461 );1462 assert!(System::events().is_empty());1463 assert!(ContractInfoOf::<Test>::get(&addr_bob)1464 .unwrap()1465 .get_alive()1466 .is_some());1467 assert_ok!(Contracts::claim_surcharge(1468 Origin::none(),1469 addr_bob.clone(),1470 Some(ALICE)1471 ));1472 assert!(ContractInfoOf::<Test>::get(&addr_bob)1473 .unwrap()1474 .get_tombstone()1475 .is_some());1476 if test_code_evicted {1477 assert_refcount!(set_rent_code_hash, 0);1478 } else {1479 assert_refcount!(set_rent_code_hash, 1);1480 }14811482 // Create another account with the address `DJANGO` with `CODE_RESTORATION`.1483 //1484 // Note that we can't use `ALICE` for creating `DJANGO` so we create yet another1485 // account `CHARLIE` and create `DJANGO` with it.1486 let _ = Balances::deposit_creating(&CHARLIE, 1_000_000);1487 assert_ok!(Contracts::instantiate_with_code(1488 Origin::signed(CHARLIE),1489 30_000,1490 GAS_LIMIT,1491 restoration_wasm,1492 vec![],1493 vec![],1494 ));1495 let addr_django = Contracts::contract_address(&CHARLIE, &restoration_code_hash, &[]);14961497 // Before performing a call to `DJANGO` save its original trie id.1498 let django_trie_id = ContractInfoOf::<Test>::get(&addr_django)1499 .unwrap()1500 .get_alive()1501 .unwrap()1502 .trie_id;15031504 // The trie is regarded as 'dirty' when it was written to in the current block.1505 if !test_restore_to_with_dirty_storage {1506 // Advance 1 block.1507 initialize_block(28);1508 }15091510 // Perform a call to `DJANGO`. This should either perform restoration successfully or1511 // fail depending on the test parameters.1512 let perform_the_restoration = || {1513 Contracts::call(1514 Origin::signed(ALICE),1515 addr_django.clone(),1516 0,1517 GAS_LIMIT,1518 set_rent_code_hash1519 .as_ref()1520 .iter()1521 .chain(AsRef::<[u8]>::as_ref(&addr_bob))1522 .cloned()1523 .collect(),1524 )1525 };15261527 // The key that is used in the restorer contract but is not in the target contract.1528 // Is supplied as delta to the restoration. We need it to check whether the key1529 // is properly removed on success but still there on failure.1530 let delta_key = {1531 let mut key = [0u8; 32];1532 key[0] = 1;1533 key1534 };15351536 if test_different_storage || test_restore_to_with_dirty_storage || test_code_evicted {1537 // Parametrization of the test imply restoration failure. Check that `DJANGO` aka1538 // restoration contract is still in place and also that `BOB` doesn't exist.1539 let result = perform_the_restoration();1540 assert!(ContractInfoOf::<Test>::get(&addr_bob)1541 .unwrap()1542 .get_tombstone()1543 .is_some());1544 let django_contract = ContractInfoOf::<Test>::get(&addr_django)1545 .unwrap()1546 .get_alive()1547 .unwrap();1548 assert_eq!(django_contract.storage_size, 8);1549 assert_eq!(django_contract.trie_id, django_trie_id);1550 assert_eq!(django_contract.deduct_block, System::block_number());1551 assert_eq!(1552 Storage::<Test>::read(&django_trie_id, &delta_key),1553 Some(vec![40, 0, 0, 0]),1554 );1555 match (1556 test_different_storage,1557 test_restore_to_with_dirty_storage,1558 test_code_evicted,1559 ) {1560 (true, false, false) => {1561 assert_err_ignore_postinfo!(result, Error::<Test>::InvalidTombstone,);1562 assert_eq!(System::events(), vec![]);1563 }1564 (_, true, false) => {1565 assert_err_ignore_postinfo!(result, Error::<Test>::InvalidContractOrigin,);1566 assert_eq!(1567 System::events(),1568 vec![1569 EventRecord {1570 phase: Phase::Initialization,1571 event: Event::pallet_contracts(crate::Event::Evicted(addr_bob)),1572 topics: vec![],1573 },1574 EventRecord {1575 phase: Phase::Initialization,1576 event: Event::frame_system(frame_system::Event::NewAccount(1577 CHARLIE1578 )),1579 topics: vec![],1580 },1581 EventRecord {1582 phase: Phase::Initialization,1583 event: Event::pallet_balances(pallet_balances::Event::Endowed(1584 CHARLIE, 1_000_0001585 )),1586 topics: vec![],1587 },1588 EventRecord {1589 phase: Phase::Initialization,1590 event: Event::frame_system(frame_system::Event::NewAccount(1591 addr_django.clone()1592 )),1593 topics: vec![],1594 },1595 EventRecord {1596 phase: Phase::Initialization,1597 event: Event::pallet_balances(pallet_balances::Event::Endowed(1598 addr_django.clone(),1599 30_0001600 )),1601 topics: vec![],1602 },1603 EventRecord {1604 phase: Phase::Initialization,1605 event: Event::pallet_balances(1606 pallet_balances::Event::Transfer(1607 CHARLIE,1608 addr_django.clone(),1609 30_0001610 )1611 ),1612 topics: vec![],1613 },1614 EventRecord {1615 phase: Phase::Initialization,1616 event: Event::pallet_contracts(crate::Event::CodeStored(1617 restoration_code_hash1618 )),1619 topics: vec![],1620 },1621 EventRecord {1622 phase: Phase::Initialization,1623 event: Event::pallet_contracts(crate::Event::Instantiated(1624 CHARLIE,1625 addr_django.clone()1626 )),1627 topics: vec![],1628 },1629 ]1630 );1631 }1632 (false, false, true) => {1633 assert_err_ignore_postinfo!(result, Error::<Test>::CodeNotFound,);1634 assert_refcount!(set_rent_code_hash, 0);1635 assert_eq!(System::events(), vec![]);1636 }1637 _ => unreachable!(),1638 }1639 } else {1640 assert_ok!(perform_the_restoration());1641 assert_refcount!(set_rent_code_hash, 2);16421643 // Here we expect that the restoration is succeeded. Check that the restoration1644 // contract `DJANGO` ceased to exist and that `BOB` returned back.1645 let bob_contract = ContractInfoOf::<Test>::get(&addr_bob)1646 .unwrap()1647 .get_alive()1648 .unwrap();1649 assert_eq!(bob_contract.rent_allowance, 50);1650 assert_eq!(bob_contract.storage_size, 4);1651 assert_eq!(bob_contract.trie_id, django_trie_id);1652 assert_eq!(bob_contract.deduct_block, System::block_number());1653 assert!(ContractInfoOf::<Test>::get(&addr_django).is_none());1654 assert_matches!(Storage::<Test>::read(&django_trie_id, &delta_key), None);1655 assert_eq!(1656 System::events(),1657 vec![1658 EventRecord {1659 phase: Phase::Initialization,1660 event: Event::pallet_contracts(crate::Event::CodeRemoved(1661 restoration_code_hash1662 )),1663 topics: vec![],1664 },1665 EventRecord {1666 phase: Phase::Initialization,1667 event: Event::frame_system(system::Event::KilledAccount(1668 addr_django.clone()1669 )),1670 topics: vec![],1671 },1672 EventRecord {1673 phase: Phase::Initialization,1674 event: Event::pallet_contracts(crate::Event::Restored(1675 addr_django,1676 addr_bob,1677 bob_contract.code_hash,1678 501679 )),1680 topics: vec![],1681 },1682 ]1683 );1684 }1685 });1686}16871688#[test]1689fn storage_max_value_limit() {1690 let (wasm, code_hash) = compile_module::<Test>("storage_size").unwrap();16911692 ExtBuilder::default()1693 .existential_deposit(50)1694 .build()1695 .execute_with(|| {1696 // Create1697 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1698 assert_ok!(Contracts::instantiate_with_code(1699 Origin::signed(ALICE),1700 30_000,1701 GAS_LIMIT,1702 wasm,1703 vec![],1704 vec![],1705 ));1706 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);1707 ContractInfoOf::<Test>::get(&addr)1708 .unwrap()1709 .get_alive()1710 .unwrap();17111712 // Call contract with allowed storage value.1713 assert_ok!(Contracts::call(1714 Origin::signed(ALICE),1715 addr.clone(),1716 0,1717 GAS_LIMIT * 2, // we are copying a huge buffer1718 <Test as Config>::MaxValueSize::get().encode(),1719 ));17201721 // Call contract with too large a storage value.1722 assert_err_ignore_postinfo!(1723 Contracts::call(1724 Origin::signed(ALICE),1725 addr,1726 0,1727 GAS_LIMIT,1728 (<Test as Config>::MaxValueSize::get() + 1).encode(),1729 ),1730 Error::<Test>::ValueTooLarge,1731 );1732 });1733}17341735#[test]1736fn deploy_and_call_other_contract() {1737 let (callee_wasm, callee_code_hash) = compile_module::<Test>("return_with_data").unwrap();1738 let (caller_wasm, caller_code_hash) = compile_module::<Test>("caller_contract").unwrap();17391740 ExtBuilder::default()1741 .existential_deposit(50)1742 .build()1743 .execute_with(|| {1744 // Create1745 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1746 assert_ok!(Contracts::instantiate_with_code(1747 Origin::signed(ALICE),1748 100_000,1749 GAS_LIMIT,1750 caller_wasm,1751 vec![],1752 vec![],1753 ));1754 assert_ok!(Contracts::instantiate_with_code(1755 Origin::signed(ALICE),1756 100_000,1757 GAS_LIMIT,1758 callee_wasm,1759 0u32.to_le_bytes().encode(),1760 vec![42],1761 ));17621763 // Call BOB contract, which attempts to instantiate and call the callee contract and1764 // makes various assertions on the results from those calls.1765 assert_ok!(Contracts::call(1766 Origin::signed(ALICE),1767 Contracts::contract_address(&ALICE, &caller_code_hash, &[]),1768 0,1769 GAS_LIMIT,1770 callee_code_hash.as_ref().to_vec(),1771 ));1772 });1773}17741775#[test]1776fn cannot_self_destruct_through_draning() {1777 let (wasm, code_hash) = compile_module::<Test>("drain").unwrap();1778 ExtBuilder::default()1779 .existential_deposit(50)1780 .build()1781 .execute_with(|| {1782 let _ = Balances::deposit_creating(&ALICE, 1_000_000);17831784 // Instantiate the BOB contract.1785 assert_ok!(Contracts::instantiate_with_code(1786 Origin::signed(ALICE),1787 100_000,1788 GAS_LIMIT,1789 wasm,1790 vec![],1791 vec![],1792 ));1793 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);17941795 // Check that the BOB contract has been instantiated.1796 assert_matches!(1797 ContractInfoOf::<Test>::get(&addr),1798 Some(ContractInfo::Alive(_))1799 );18001801 // Call BOB which makes it send all funds to the zero address1802 // The contract code asserts that the correct error value is returned.1803 assert_ok!(Contracts::call(1804 Origin::signed(ALICE),1805 addr,1806 0,1807 GAS_LIMIT,1808 vec![],1809 ));1810 });1811}18121813#[test]1814fn cannot_self_destruct_while_live() {1815 let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();1816 ExtBuilder::default()1817 .existential_deposit(50)1818 .build()1819 .execute_with(|| {1820 let _ = Balances::deposit_creating(&ALICE, 1_000_000);18211822 // Instantiate the BOB contract.1823 assert_ok!(Contracts::instantiate_with_code(1824 Origin::signed(ALICE),1825 100_000,1826 GAS_LIMIT,1827 wasm,1828 vec![],1829 vec![],1830 ));1831 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);18321833 // Check that the BOB contract has been instantiated.1834 assert_matches!(1835 ContractInfoOf::<Test>::get(&addr),1836 Some(ContractInfo::Alive(_))1837 );18381839 // Call BOB with input data, forcing it make a recursive call to itself to1840 // self-destruct, resulting in a trap.1841 assert_err_ignore_postinfo!(1842 Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![0],),1843 Error::<Test>::ContractTrapped,1844 );18451846 // Check that BOB is still alive.1847 assert_matches!(1848 ContractInfoOf::<Test>::get(&addr),1849 Some(ContractInfo::Alive(_))1850 );1851 });1852}18531854#[test]1855fn self_destruct_works() {1856 let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();1857 ExtBuilder::default()1858 .existential_deposit(50)1859 .build()1860 .execute_with(|| {1861 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1862 let _ = Balances::deposit_creating(&DJANGO, 1_000_000);18631864 // Instantiate the BOB contract.1865 assert_ok!(Contracts::instantiate_with_code(1866 Origin::signed(ALICE),1867 100_000,1868 GAS_LIMIT,1869 wasm,1870 vec![],1871 vec![],1872 ));1873 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);18741875 // Check that the BOB contract has been instantiated.1876 assert_matches!(1877 ContractInfoOf::<Test>::get(&addr),1878 Some(ContractInfo::Alive(_))1879 );18801881 // Drop all previous events1882 initialize_block(2);18831884 // Call BOB without input data which triggers termination.1885 assert_matches!(1886 Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),1887 Ok(_)1888 );18891890 pretty_assertions::assert_eq!(1891 System::events(),1892 vec![1893 EventRecord {1894 phase: Phase::Initialization,1895 event: Event::frame_system(frame_system::Event::KilledAccount(1896 addr.clone()1897 )),1898 topics: vec![],1899 },1900 EventRecord {1901 phase: Phase::Initialization,1902 event: Event::pallet_balances(pallet_balances::Event::Transfer(1903 addr.clone(),1904 DJANGO,1905 93_0861906 )),1907 topics: vec![],1908 },1909 EventRecord {1910 phase: Phase::Initialization,1911 event: Event::pallet_contracts(crate::Event::CodeRemoved(code_hash)),1912 topics: vec![],1913 },1914 EventRecord {1915 phase: Phase::Initialization,1916 event: Event::pallet_contracts(crate::Event::Terminated(1917 addr.clone(),1918 DJANGO1919 )),1920 topics: vec![],1921 },1922 ]1923 );19241925 // Check that account is gone1926 assert!(ContractInfoOf::<Test>::get(&addr).is_none());19271928 // check that the beneficiary (django) got remaining balance1929 // some rent was deducted before termination1930 assert_eq!(Balances::free_balance(DJANGO), 1_093_086);1931 });1932}19331934// This tests that one contract cannot prevent another from self-destructing by sending it1935// additional funds after it has been drained.1936#[test]1937fn destroy_contract_and_transfer_funds() {1938 let (callee_wasm, callee_code_hash) = compile_module::<Test>("self_destruct").unwrap();1939 let (caller_wasm, caller_code_hash) = compile_module::<Test>("destroy_and_transfer").unwrap();19401941 ExtBuilder::default()1942 .existential_deposit(50)1943 .build()1944 .execute_with(|| {1945 // Create1946 let _ = Balances::deposit_creating(&ALICE, 1_000_000);1947 assert_ok!(Contracts::instantiate_with_code(1948 Origin::signed(ALICE),1949 200_000,1950 GAS_LIMIT,1951 callee_wasm,1952 vec![],1953 vec![42]1954 ));19551956 // This deploys the BOB contract, which in turn deploys the CHARLIE contract during1957 // construction.1958 assert_ok!(Contracts::instantiate_with_code(1959 Origin::signed(ALICE),1960 200_000,1961 GAS_LIMIT,1962 caller_wasm,1963 callee_code_hash.as_ref().to_vec(),1964 vec![],1965 ));1966 let addr_bob = Contracts::contract_address(&ALICE, &caller_code_hash, &[]);1967 let addr_charlie =1968 Contracts::contract_address(&addr_bob, &callee_code_hash, &[0x47, 0x11]);19691970 // Check that the CHARLIE contract has been instantiated.1971 assert_matches!(1972 ContractInfoOf::<Test>::get(&addr_charlie),1973 Some(ContractInfo::Alive(_))1974 );19751976 // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.1977 assert_ok!(Contracts::call(1978 Origin::signed(ALICE),1979 addr_bob,1980 0,1981 GAS_LIMIT,1982 addr_charlie.encode(),1983 ));19841985 // Check that CHARLIE has moved on to the great beyond (ie. died).1986 assert!(ContractInfoOf::<Test>::get(&addr_charlie).is_none());1987 });1988}19891990#[test]1991fn cannot_self_destruct_in_constructor() {1992 let (wasm, _) = compile_module::<Test>("self_destructing_constructor").unwrap();1993 ExtBuilder::default()1994 .existential_deposit(50)1995 .build()1996 .execute_with(|| {1997 let _ = Balances::deposit_creating(&ALICE, 1_000_000);19981999 // Fail to instantiate the BOB because the contructor calls seal_terminate.2000 assert_err_ignore_postinfo!(2001 Contracts::instantiate_with_code(2002 Origin::signed(ALICE),2003 100_000,2004 GAS_LIMIT,2005 wasm,2006 vec![],2007 vec![],2008 ),2009 Error::<Test>::NotCallable,2010 );2011 });2012}20132014#[test]2015fn crypto_hashes() {2016 let (wasm, code_hash) = compile_module::<Test>("crypto_hashes").unwrap();20172018 ExtBuilder::default()2019 .existential_deposit(50)2020 .build()2021 .execute_with(|| {2022 let _ = Balances::deposit_creating(&ALICE, 1_000_000);20232024 // Instantiate the CRYPTO_HASHES contract.2025 assert_ok!(Contracts::instantiate_with_code(2026 Origin::signed(ALICE),2027 100_000,2028 GAS_LIMIT,2029 wasm,2030 vec![],2031 vec![],2032 ));2033 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);2034 // Perform the call.2035 let input = b"_DEAD_BEEF";2036 use sp_io::hashing::*;2037 // Wraps a hash function into a more dynamic form usable for testing.2038 macro_rules! dyn_hash_fn {2039 ($name:ident) => {2040 Box::new(|input| $name(input).as_ref().to_vec().into_boxed_slice())2041 };2042 }2043 // All hash functions and their associated output byte lengths.2044 let test_cases: &[(Box<dyn Fn(&[u8]) -> Box<[u8]>>, usize)] = &[2045 (dyn_hash_fn!(sha2_256), 32),2046 (dyn_hash_fn!(keccak_256), 32),2047 (dyn_hash_fn!(blake2_256), 32),2048 (dyn_hash_fn!(blake2_128), 16),2049 ];2050 // Test the given hash functions for the input: "_DEAD_BEEF"2051 for (n, (hash_fn, expected_size)) in test_cases.iter().enumerate() {2052 // We offset data in the contract tables by 1.2053 let mut params = vec![(n + 1) as u8];2054 params.extend_from_slice(input);2055 let result = <Pallet<Test>>::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, params)2056 .result2057 .unwrap();2058 assert!(result.is_success());2059 let expected = hash_fn(input.as_ref());2060 assert_eq!(&result.data[..*expected_size], &*expected);2061 }2062 })2063}20642065#[test]2066fn transfer_return_code() {2067 let (wasm, code_hash) = compile_module::<Test>("transfer_return_code").unwrap();2068 ExtBuilder::default()2069 .existential_deposit(50)2070 .build()2071 .execute_with(|| {2072 let subsistence = Pallet::<Test>::subsistence_threshold();2073 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);20742075 assert_ok!(Contracts::instantiate_with_code(2076 Origin::signed(ALICE),2077 subsistence * 100,2078 GAS_LIMIT,2079 wasm,2080 vec![],2081 vec![],2082 ),);2083 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);20842085 // Contract has only the minimal balance so any transfer will return BelowSubsistence.2086 Balances::make_free_balance_be(&addr, subsistence);2087 let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![])2088 .result2089 .unwrap();2090 assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);20912092 // Contract has enough total balance in order to not go below the subsistence2093 // threshold when transfering 100 balance but this balance is reserved so2094 // the transfer still fails but with another return code.2095 Balances::make_free_balance_be(&addr, subsistence + 100);2096 Balances::reserve(&addr, subsistence + 100).unwrap();2097 let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, vec![])2098 .result2099 .unwrap();2100 assert_return_code!(result, RuntimeReturnCode::TransferFailed);2101 });2102}21032104#[test]2105fn call_return_code() {2106 let (caller_code, caller_hash) = compile_module::<Test>("call_return_code").unwrap();2107 let (callee_code, callee_hash) = compile_module::<Test>("ok_trap_revert").unwrap();2108 ExtBuilder::default()2109 .existential_deposit(50)2110 .build()2111 .execute_with(|| {2112 let subsistence = Pallet::<Test>::subsistence_threshold();2113 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);2114 let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence);21152116 assert_ok!(Contracts::instantiate_with_code(2117 Origin::signed(ALICE),2118 subsistence * 100,2119 GAS_LIMIT,2120 caller_code,2121 vec![0],2122 vec![],2123 ),);2124 let addr_bob = Contracts::contract_address(&ALICE, &caller_hash, &[]);2125 Balances::make_free_balance_be(&addr_bob, subsistence);21262127 // Contract calls into Django which is no valid contract2128 let result = Contracts::bare_call(2129 ALICE,2130 addr_bob.clone(),2131 0,2132 GAS_LIMIT,2133 AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),2134 )2135 .result2136 .unwrap();2137 assert_return_code!(result, RuntimeReturnCode::NotCallable);21382139 assert_ok!(Contracts::instantiate_with_code(2140 Origin::signed(CHARLIE),2141 subsistence * 100,2142 GAS_LIMIT,2143 callee_code,2144 vec![0],2145 vec![],2146 ),);2147 let addr_django = Contracts::contract_address(&CHARLIE, &callee_hash, &[]);2148 Balances::make_free_balance_be(&addr_django, subsistence);21492150 // Contract has only the minimal balance so any transfer will return BelowSubsistence.2151 let result = Contracts::bare_call(2152 ALICE,2153 addr_bob.clone(),2154 0,2155 GAS_LIMIT,2156 AsRef::<[u8]>::as_ref(&addr_django)2157 .iter()2158 .chain(&0u32.to_le_bytes())2159 .cloned()2160 .collect(),2161 )2162 .result2163 .unwrap();2164 assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);21652166 // Contract has enough total balance in order to not go below the subsistence2167 // threshold when transfering 100 balance but this balance is reserved so2168 // the transfer still fails but with another return code.2169 Balances::make_free_balance_be(&addr_bob, subsistence + 100);2170 Balances::reserve(&addr_bob, subsistence + 100).unwrap();2171 let result = Contracts::bare_call(2172 ALICE,2173 addr_bob.clone(),2174 0,2175 GAS_LIMIT,2176 AsRef::<[u8]>::as_ref(&addr_django)2177 .iter()2178 .chain(&0u32.to_le_bytes())2179 .cloned()2180 .collect(),2181 )2182 .result2183 .unwrap();2184 assert_return_code!(result, RuntimeReturnCode::TransferFailed);21852186 // Contract has enough balance but callee reverts because "1" is passed.2187 Balances::make_free_balance_be(&addr_bob, subsistence + 1000);2188 let result = Contracts::bare_call(2189 ALICE,2190 addr_bob.clone(),2191 0,2192 GAS_LIMIT,2193 AsRef::<[u8]>::as_ref(&addr_django)2194 .iter()2195 .chain(&1u32.to_le_bytes())2196 .cloned()2197 .collect(),2198 )2199 .result2200 .unwrap();2201 assert_return_code!(result, RuntimeReturnCode::CalleeReverted);22022203 // Contract has enough balance but callee traps because "2" is passed.2204 let result = Contracts::bare_call(2205 ALICE,2206 addr_bob,2207 0,2208 GAS_LIMIT,2209 AsRef::<[u8]>::as_ref(&addr_django)2210 .iter()2211 .chain(&2u32.to_le_bytes())2212 .cloned()2213 .collect(),2214 )2215 .result2216 .unwrap();2217 assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);2218 });2219}22202221#[test]2222fn instantiate_return_code() {2223 let (caller_code, caller_hash) = compile_module::<Test>("instantiate_return_code").unwrap();2224 let (callee_code, callee_hash) = compile_module::<Test>("ok_trap_revert").unwrap();2225 ExtBuilder::default()2226 .existential_deposit(50)2227 .build()2228 .execute_with(|| {2229 let subsistence = Pallet::<Test>::subsistence_threshold();2230 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);2231 let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence);2232 let callee_hash = callee_hash.as_ref().to_vec();22332234 assert_ok!(Contracts::instantiate_with_code(2235 Origin::signed(ALICE),2236 subsistence * 100,2237 GAS_LIMIT,2238 callee_code,2239 vec![],2240 vec![],2241 ),);22422243 assert_ok!(Contracts::instantiate_with_code(2244 Origin::signed(ALICE),2245 subsistence * 100,2246 GAS_LIMIT,2247 caller_code,2248 vec![],2249 vec![],2250 ),);2251 let addr = Contracts::contract_address(&ALICE, &caller_hash, &[]);22522253 // Contract has only the minimal balance so any transfer will return BelowSubsistence.2254 Balances::make_free_balance_be(&addr, subsistence);2255 let result =2256 Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone())2257 .result2258 .unwrap();2259 assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);22602261 // Contract has enough total balance in order to not go below the subsistence2262 // threshold when transfering the balance but this balance is reserved so2263 // the transfer still fails but with another return code.2264 Balances::make_free_balance_be(&addr, subsistence + 10_000);2265 Balances::reserve(&addr, subsistence + 10_000).unwrap();2266 let result =2267 Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, callee_hash.clone())2268 .result2269 .unwrap();2270 assert_return_code!(result, RuntimeReturnCode::TransferFailed);22712272 // Contract has enough balance but the passed code hash is invalid2273 Balances::make_free_balance_be(&addr, subsistence + 10_000);2274 let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0; 33])2275 .result2276 .unwrap();2277 assert_return_code!(result, RuntimeReturnCode::CodeNotFound);22782279 // Contract has enough balance but callee reverts because "1" is passed.2280 let result = Contracts::bare_call(2281 ALICE,2282 addr.clone(),2283 0,2284 GAS_LIMIT,2285 callee_hash2286 .iter()2287 .chain(&1u32.to_le_bytes())2288 .cloned()2289 .collect(),2290 )2291 .result2292 .unwrap();2293 assert_return_code!(result, RuntimeReturnCode::CalleeReverted);22942295 // Contract has enough balance but callee traps because "2" is passed.2296 let result = Contracts::bare_call(2297 ALICE,2298 addr,2299 0,2300 GAS_LIMIT,2301 callee_hash2302 .iter()2303 .chain(&2u32.to_le_bytes())2304 .cloned()2305 .collect(),2306 )2307 .result2308 .unwrap();2309 assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);2310 });2311}23122313#[test]2314fn disabled_chain_extension_wont_deploy() {2315 let (code, _hash) = compile_module::<Test>("chain_extension").unwrap();2316 ExtBuilder::default()2317 .existential_deposit(50)2318 .build()2319 .execute_with(|| {2320 let subsistence = Pallet::<Test>::subsistence_threshold();2321 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);2322 TestExtension::disable();2323 assert_err_ignore_postinfo!(2324 Contracts::instantiate_with_code(2325 Origin::signed(ALICE),2326 3 * subsistence,2327 GAS_LIMIT,2328 code,2329 vec![],2330 vec![],2331 ),2332 "module uses chain extensions but chain extensions are disabled",2333 );2334 });2335}23362337#[test]2338fn disabled_chain_extension_errors_on_call() {2339 let (code, hash) = compile_module::<Test>("chain_extension").unwrap();2340 ExtBuilder::default()2341 .existential_deposit(50)2342 .build()2343 .execute_with(|| {2344 let subsistence = Pallet::<Test>::subsistence_threshold();2345 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);2346 assert_ok!(Contracts::instantiate_with_code(2347 Origin::signed(ALICE),2348 subsistence * 100,2349 GAS_LIMIT,2350 code,2351 vec![],2352 vec![],2353 ),);2354 let addr = Contracts::contract_address(&ALICE, &hash, &[]);2355 TestExtension::disable();2356 assert_err_ignore_postinfo!(2357 Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),2358 Error::<Test>::NoChainExtension,2359 );2360 });2361}23622363#[test]2364fn chain_extension_works() {2365 let (code, hash) = compile_module::<Test>("chain_extension").unwrap();2366 ExtBuilder::default()2367 .existential_deposit(50)2368 .build()2369 .execute_with(|| {2370 let subsistence = Pallet::<Test>::subsistence_threshold();2371 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);2372 assert_ok!(Contracts::instantiate_with_code(2373 Origin::signed(ALICE),2374 subsistence * 100,2375 GAS_LIMIT,2376 code,2377 vec![],2378 vec![],2379 ),);2380 let addr = Contracts::contract_address(&ALICE, &hash, &[]);23812382 // The contract takes a up to 2 byte buffer where the first byte passed is used as2383 // as func_id to the chain extension which behaves differently based on the2384 // func_id.23852386 // 0 = read input buffer and pass it through as output2387 let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![0, 99]);2388 let gas_consumed = result.gas_consumed;2389 assert_eq!(TestExtension::last_seen_buffer(), vec![0, 99]);2390 assert_eq!(result.result.unwrap().data, Bytes(vec![0, 99]));23912392 // 1 = treat inputs as integer primitives and store the supplied integers2393 Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![1])2394 .result2395 .unwrap();2396 // those values passed in the fixture2397 assert_eq!(TestExtension::last_seen_inputs(), (4, 1, 16, 12));23982399 // 2 = charge some extra weight (amount supplied in second byte)2400 let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![2, 42]);2401 assert_ok!(result.result);2402 assert_eq!(result.gas_consumed, gas_consumed + 42);24032404 // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer2405 let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, vec![3])2406 .result2407 .unwrap();2408 assert_eq!(result.flags, ReturnFlags::REVERT);2409 assert_eq!(result.data, Bytes(vec![42, 99]));2410 });2411}24122413#[test]2414fn lazy_removal_works() {2415 let (code, hash) = compile_module::<Test>("self_destruct").unwrap();2416 ExtBuilder::default()2417 .existential_deposit(50)2418 .build()2419 .execute_with(|| {2420 let subsistence = Pallet::<Test>::subsistence_threshold();2421 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);24222423 assert_ok!(Contracts::instantiate_with_code(2424 Origin::signed(ALICE),2425 subsistence * 100,2426 GAS_LIMIT,2427 code,2428 vec![],2429 vec![],2430 ),);24312432 let addr = Contracts::contract_address(&ALICE, &hash, &[]);2433 let info = <ContractInfoOf<Test>>::get(&addr)2434 .unwrap()2435 .get_alive()2436 .unwrap();2437 let trie = &info.child_trie_info();24382439 // Put value into the contracts child trie2440 child::put(trie, &[99], &42);24412442 // Terminate the contract2443 assert_ok!(Contracts::call(2444 Origin::signed(ALICE),2445 addr.clone(),2446 0,2447 GAS_LIMIT,2448 vec![],2449 ));24502451 // Contract info should be gone2452 assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));24532454 // But value should be still there as the lazy removal did not run, yet.2455 assert_matches!(child::get(trie, &[99]), Some(42));24562457 // Run the lazy removal2458 Contracts::on_initialize(Weight::max_value());24592460 // Value should be gone now2461 assert_matches!(child::get::<i32>(trie, &[99]), None);2462 });2463}24642465#[test]2466fn lazy_removal_partial_remove_works() {2467 let (code, hash) = compile_module::<Test>("self_destruct").unwrap();24682469 // We create a contract with some extra keys above the weight limit2470 let extra_keys = 7u32;2471 let weight_limit = 5_000_000_000;2472 let (_, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);2473 let vals: Vec<_> = (0..max_keys + extra_keys)2474 .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))2475 .collect();24762477 let mut ext = ExtBuilder::default().existential_deposit(50).build();24782479 let trie = ext.execute_with(|| {2480 let subsistence = Pallet::<Test>::subsistence_threshold();2481 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);24822483 assert_ok!(Contracts::instantiate_with_code(2484 Origin::signed(ALICE),2485 subsistence * 100,2486 GAS_LIMIT,2487 code,2488 vec![],2489 vec![],2490 ),);24912492 let addr = Contracts::contract_address(&ALICE, &hash, &[]);2493 let info = <ContractInfoOf<Test>>::get(&addr)2494 .unwrap()2495 .get_alive()2496 .unwrap();2497 let trie = &info.child_trie_info();24982499 // Put value into the contracts child trie2500 for val in &vals {2501 Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();2502 }25032504 // Terminate the contract2505 assert_ok!(Contracts::call(2506 Origin::signed(ALICE),2507 addr.clone(),2508 0,2509 GAS_LIMIT,2510 vec![],2511 ));25122513 // Contract info should be gone2514 assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));25152516 // But value should be still there as the lazy removal did not run, yet.2517 for val in &vals {2518 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));2519 }25202521 trie.clone()2522 });25232524 // The lazy removal limit only applies to the backend but not to the overlay.2525 // This commits all keys from the overlay to the backend.2526 ext.commit_all().unwrap();25272528 ext.execute_with(|| {2529 // Run the lazy removal2530 let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);25312532 // Weight should be exhausted because we could not even delete all keys2533 assert_eq!(weight_used, weight_limit);25342535 let mut num_deleted = 0u32;2536 let mut num_remaining = 0u32;25372538 for val in &vals {2539 match child::get::<u32>(&trie, &blake2_256(&val.0)) {2540 None => num_deleted += 1,2541 Some(x) if x == val.1 => num_remaining += 1,2542 Some(_) => panic!("Unexpected value in contract storage"),2543 }2544 }25452546 // All but one key is removed2547 assert_eq!(num_deleted + num_remaining, vals.len() as u32);2548 assert_eq!(num_deleted, max_keys);2549 assert_eq!(num_remaining, extra_keys);2550 });2551}25522553#[test]2554fn lazy_removal_does_no_run_on_full_block() {2555 let (code, hash) = compile_module::<Test>("self_destruct").unwrap();2556 ExtBuilder::default()2557 .existential_deposit(50)2558 .build()2559 .execute_with(|| {2560 let subsistence = Pallet::<Test>::subsistence_threshold();2561 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);25622563 assert_ok!(Contracts::instantiate_with_code(2564 Origin::signed(ALICE),2565 subsistence * 100,2566 GAS_LIMIT,2567 code,2568 vec![],2569 vec![],2570 ),);25712572 let addr = Contracts::contract_address(&ALICE, &hash, &[]);2573 let info = <ContractInfoOf<Test>>::get(&addr)2574 .unwrap()2575 .get_alive()2576 .unwrap();2577 let trie = &info.child_trie_info();2578 let max_keys = 30;25792580 // Create some storage items for the contract.2581 let vals: Vec<_> = (0..max_keys)2582 .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))2583 .collect();25842585 // Put value into the contracts child trie2586 for val in &vals {2587 Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();2588 }25892590 // Terminate the contract2591 assert_ok!(Contracts::call(2592 Origin::signed(ALICE),2593 addr.clone(),2594 0,2595 GAS_LIMIT,2596 vec![],2597 ));25982599 // Contract info should be gone2600 assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));26012602 // But value should be still there as the lazy removal did not run, yet.2603 for val in &vals {2604 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));2605 }26062607 // Fill up the block which should prevent the lazy storage removal from running.2608 System::register_extra_weight_unchecked(2609 <Test as system::Config>::BlockWeights::get().max_block,2610 DispatchClass::Mandatory,2611 );26122613 // Run the lazy removal without any limit so that all keys would be removed if there2614 // had been some weight left in the block.2615 let weight_used = Contracts::on_initialize(Weight::max_value());2616 let base = <<Test as Config>::WeightInfo as WeightInfo>::on_initialize();2617 assert_eq!(weight_used, base);26182619 // All the keys are still in place2620 for val in &vals {2621 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));2622 }26232624 // Run the lazy removal directly which disregards the block limits2625 Storage::<Test>::process_deletion_queue_batch(Weight::max_value());26262627 // Now the keys should be gone2628 for val in &vals {2629 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);2630 }2631 });2632}26332634#[test]2635fn lazy_removal_does_not_use_all_weight() {2636 let (code, hash) = compile_module::<Test>("self_destruct").unwrap();2637 ExtBuilder::default()2638 .existential_deposit(50)2639 .build()2640 .execute_with(|| {2641 let subsistence = Pallet::<Test>::subsistence_threshold();2642 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);26432644 assert_ok!(Contracts::instantiate_with_code(2645 Origin::signed(ALICE),2646 subsistence * 100,2647 GAS_LIMIT,2648 code,2649 vec![],2650 vec![],2651 ),);26522653 let addr = Contracts::contract_address(&ALICE, &hash, &[]);2654 let info = <ContractInfoOf<Test>>::get(&addr)2655 .unwrap()2656 .get_alive()2657 .unwrap();2658 let trie = &info.child_trie_info();2659 let weight_limit = 5_000_000_000;2660 let (weight_per_key, max_keys) = Storage::<Test>::deletion_budget(1, weight_limit);26612662 // We create a contract with one less storage item than we can remove within the limit2663 let vals: Vec<_> = (0..max_keys - 1)2664 .map(|i| (blake2_256(&i.encode()), (i as u32), (i as u32).encode()))2665 .collect();26662667 // Put value into the contracts child trie2668 for val in &vals {2669 Storage::<Test>::write(&addr, &info.trie_id, &val.0, Some(val.2.clone())).unwrap();2670 }26712672 // Terminate the contract2673 assert_ok!(Contracts::call(2674 Origin::signed(ALICE),2675 addr.clone(),2676 0,2677 GAS_LIMIT,2678 vec![],2679 ));26802681 // Contract info should be gone2682 assert!(!<ContractInfoOf::<Test>>::contains_key(&addr));26832684 // But value should be still there as the lazy removal did not run, yet.2685 for val in &vals {2686 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), Some(val.1));2687 }26882689 // Run the lazy removal2690 let weight_used = Storage::<Test>::process_deletion_queue_batch(weight_limit);26912692 // We have one less key in our trie than our weight limit suffices for2693 assert_eq!(weight_used, weight_limit - weight_per_key);26942695 // All the keys are removed2696 for val in vals {2697 assert_eq!(child::get::<u32>(trie, &blake2_256(&val.0)), None);2698 }2699 });2700}27012702#[test]2703fn deletion_queue_full() {2704 let (code, hash) = compile_module::<Test>("self_destruct").unwrap();2705 ExtBuilder::default()2706 .existential_deposit(50)2707 .build()2708 .execute_with(|| {2709 let subsistence = Pallet::<Test>::subsistence_threshold();2710 let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence);27112712 assert_ok!(Contracts::instantiate_with_code(2713 Origin::signed(ALICE),2714 subsistence * 100,2715 GAS_LIMIT,2716 code,2717 vec![],2718 vec![],2719 ),);27202721 let addr = Contracts::contract_address(&ALICE, &hash, &[]);27222723 // fill the deletion queue up until its limit2724 Storage::<Test>::fill_queue_with_dummies();27252726 // Terminate the contract should fail2727 assert_err_ignore_postinfo!(2728 Contracts::call(Origin::signed(ALICE), addr.clone(), 0, GAS_LIMIT, vec![],),2729 Error::<Test>::DeletionQueueFull,2730 );27312732 // Contract should be alive because removal failed2733 <ContractInfoOf<Test>>::get(&addr)2734 .unwrap()2735 .get_alive()2736 .unwrap();27372738 // make the contract ripe for eviction2739 initialize_block(5);27402741 // eviction should fail for the same reason as termination2742 assert_err!(2743 Contracts::claim_surcharge(Origin::none(), addr.clone(), Some(ALICE)),2744 Error::<Test>::DeletionQueueFull,2745 );27462747 // Contract should be alive because removal failed2748 <ContractInfoOf<Test>>::get(&addr)2749 .unwrap()2750 .get_alive()2751 .unwrap();2752 });2753}27542755#[test]2756fn not_deployed_if_endowment_too_low_for_first_rent() {2757 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();27582759 // The instantiation deducted the rent for one block immediately2760 let first_rent = <Test as Config>::RentFraction::get()2761 // base_deposit + deploy_set_storage (4 bytes in 1 item) - free_balance2762 .mul_ceil(80_000u32 + 40_000 + 10_000 - 30_000)2763 // blocks to rent2764 * 1;27652766 ExtBuilder::default()2767 .existential_deposit(50)2768 .build()2769 .execute_with(|| {2770 // Create2771 let _ = Balances::deposit_creating(&ALICE, 1_000_000);2772 assert_storage_noop!(assert_err_ignore_postinfo!(2773 Contracts::instantiate_with_code(2774 Origin::signed(ALICE),2775 30_000,2776 GAS_LIMIT,2777 wasm,2778 (BalanceOf::<Test>::from(first_rent) - BalanceOf::<Test>::from(1u32)).encode(), // rent allowance2779 vec![],2780 ),2781 Error::<Test>::NewContractNotFunded,2782 ));2783 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);2784 assert_matches!(ContractInfoOf::<Test>::get(&addr), None);2785 });2786}27872788#[test]2789fn surcharge_reward_is_capped() {2790 let (wasm, code_hash) = compile_module::<Test>("set_rent").unwrap();2791 ExtBuilder::default()2792 .existential_deposit(50)2793 .build()2794 .execute_with(|| {2795 let _ = Balances::deposit_creating(&ALICE, 1_000_000);2796 assert_ok!(Contracts::instantiate_with_code(2797 Origin::signed(ALICE),2798 30_000,2799 GAS_LIMIT,2800 wasm,2801 <BalanceOf<Test>>::from(10_000u32).encode(), // rent allowance2802 vec![],2803 ));2804 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);2805 let contract = <ContractInfoOf<Test>>::get(&addr)2806 .unwrap()2807 .get_alive()2808 .unwrap();2809 let balance = Balances::free_balance(&ALICE);2810 let reward = <Test as Config>::SurchargeReward::get();28112812 // some rent should have payed due to instantiation2813 assert_ne!(contract.rent_payed, 0);28142815 // the reward should be parameterized sufficiently high to make this test useful2816 assert!(reward > contract.rent_payed);28172818 // make contract eligible for eviction2819 initialize_block(40);28202821 // this should have removed the contract2822 assert_ok!(Contracts::claim_surcharge(2823 Origin::none(),2824 addr.clone(),2825 Some(ALICE)2826 ));28272828 // this reward does not take into account the last rent payment collected during eviction2829 let capped_reward = reward.min(contract.rent_payed);28302831 // this is smaller than the actual reward because it does not take into account the2832 // rent collected during eviction2833 assert!(Balances::free_balance(&ALICE) > balance + capped_reward);28342835 // the full reward is not payed out because of the cap introduced by rent_payed2836 assert!(Balances::free_balance(&ALICE) < balance + reward);2837 });2838}28392840#[test]2841fn refcounter() {2842 let (wasm, code_hash) = compile_module::<Test>("self_destruct").unwrap();2843 ExtBuilder::default()2844 .existential_deposit(50)2845 .build()2846 .execute_with(|| {2847 let _ = Balances::deposit_creating(&ALICE, 1_000_000);2848 let subsistence = Pallet::<Test>::subsistence_threshold();28492850 // Create two contracts with the same code and check that they do in fact share it.2851 assert_ok!(Contracts::instantiate_with_code(2852 Origin::signed(ALICE),2853 subsistence * 100,2854 GAS_LIMIT,2855 wasm.clone(),2856 vec![],2857 vec![0],2858 ));2859 assert_ok!(Contracts::instantiate_with_code(2860 Origin::signed(ALICE),2861 subsistence * 100,2862 GAS_LIMIT,2863 wasm.clone(),2864 vec![],2865 vec![1],2866 ));2867 assert_refcount!(code_hash, 2);28682869 // Sharing should also work with the usual instantiate call2870 assert_ok!(Contracts::instantiate(2871 Origin::signed(ALICE),2872 subsistence * 100,2873 GAS_LIMIT,2874 code_hash,2875 vec![],2876 vec![2],2877 ));2878 assert_refcount!(code_hash, 3);28792880 // addresses of all three existing contracts2881 let addr0 = Contracts::contract_address(&ALICE, &code_hash, &[0]);2882 let addr1 = Contracts::contract_address(&ALICE, &code_hash, &[1]);2883 let addr2 = Contracts::contract_address(&ALICE, &code_hash, &[2]);28842885 // Terminating one contract should decrement the refcount2886 assert_ok!(Contracts::call(2887 Origin::signed(ALICE),2888 addr0,2889 0,2890 GAS_LIMIT,2891 vec![],2892 ));2893 assert_refcount!(code_hash, 2);28942895 // make remaining contracts eligible for eviction2896 initialize_block(40);28972898 // remove one of them2899 assert_ok!(Contracts::claim_surcharge(2900 Origin::none(),2901 addr1,2902 Some(ALICE)2903 ));2904 assert_refcount!(code_hash, 1);29052906 // Pristine code should still be there2907 crate::PristineCode::<Test>::get(code_hash).unwrap();29082909 // remove the last contract2910 assert_ok!(Contracts::claim_surcharge(2911 Origin::none(),2912 addr2,2913 Some(ALICE)2914 ));2915 assert_refcount!(code_hash, 0);29162917 // all code should be gone2918 assert_matches!(crate::PristineCode::<Test>::get(code_hash), None);2919 assert_matches!(crate::CodeStorage::<Test>::get(code_hash), None);2920 });2921}29222923#[test]2924fn reinstrument_does_charge() {2925 let (wasm, code_hash) = compile_module::<Test>("return_with_data").unwrap();2926 ExtBuilder::default()2927 .existential_deposit(50)2928 .build()2929 .execute_with(|| {2930 let _ = Balances::deposit_creating(&ALICE, 1_000_000);2931 let subsistence = Pallet::<Test>::subsistence_threshold();2932 let zero = 0u32.to_le_bytes().encode();2933 let code_len = wasm.len() as u32;29342935 assert_ok!(Contracts::instantiate_with_code(2936 Origin::signed(ALICE),2937 subsistence * 100,2938 GAS_LIMIT,2939 wasm,2940 zero.clone(),2941 vec![],2942 ));29432944 let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);29452946 // Call the contract two times without reinstrument29472948 let result0 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());2949 assert!(result0.result.unwrap().is_success());29502951 let result1 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());2952 assert!(result1.result.unwrap().is_success());29532954 // They should match because both where called with the same schedule.2955 assert_eq!(result0.gas_consumed, result1.gas_consumed);29562957 // Update the schedule version but keep the rest the same2958 crate::CurrentSchedule::mutate(|old: &mut Schedule<Test>| {2959 old.version += 1;2960 });29612962 // This call should trigger reinstrumentation2963 let result2 = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, zero.clone());2964 assert!(result2.result.unwrap().is_success());2965 assert!(result2.gas_consumed > result1.gas_consumed);2966 assert_eq!(2967 result2.gas_consumed,2968 result1.gas_consumed + <Test as Config>::WeightInfo::instrument(code_len / 1024),2969 );2970 });2971}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))
- }
-}