mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 19:15:41 +00:00
contracts: Move Schedule from Storage to Config (#8773)
* Move `Schedule` from Storage to Config * Updated CHANGELOG * Fix nits from review * Fix migration * Print the debug buffer as tracing message * Use `debug` instead of `trace` and update README * Add additional assert to test * Rename `schedule_version` to `instruction_weights_version` * Fixed typo * Added more comments to wat fixtures * Add clarification for the `debug_message` field
This commit is contained in:
committed by
GitHub
parent
3c0270fe57
commit
1ac95b6ba6
@@ -22,7 +22,7 @@ use sc_chain_spec::ChainSpecExtension;
|
|||||||
use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519};
|
use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use node_runtime::{
|
use node_runtime::{
|
||||||
AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig,
|
AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, CouncilConfig,
|
||||||
DemocracyConfig, GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus,
|
DemocracyConfig, GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus,
|
||||||
StakingConfig, ElectionsConfig, IndicesConfig, SocietyConfig, SudoConfig, SystemConfig,
|
StakingConfig, ElectionsConfig, IndicesConfig, SocietyConfig, SudoConfig, SystemConfig,
|
||||||
TechnicalCommitteeConfig, wasm_binary_unwrap, MAX_NOMINATIONS,
|
TechnicalCommitteeConfig, wasm_binary_unwrap, MAX_NOMINATIONS,
|
||||||
@@ -146,7 +146,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
|
|||||||
|
|
||||||
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
|
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
|
||||||
|
|
||||||
testnet_genesis(initial_authorities, vec![], root_key, Some(endowed_accounts), false)
|
testnet_genesis(initial_authorities, vec![], root_key, Some(endowed_accounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Staging testnet config.
|
/// Staging testnet config.
|
||||||
@@ -212,7 +212,6 @@ pub fn testnet_genesis(
|
|||||||
initial_nominators: Vec<AccountId>,
|
initial_nominators: Vec<AccountId>,
|
||||||
root_key: AccountId,
|
root_key: AccountId,
|
||||||
endowed_accounts: Option<Vec<AccountId>>,
|
endowed_accounts: Option<Vec<AccountId>>,
|
||||||
enable_println: bool,
|
|
||||||
) -> GenesisConfig {
|
) -> GenesisConfig {
|
||||||
let mut endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
|
let mut endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
|
||||||
vec![
|
vec![
|
||||||
@@ -308,11 +307,6 @@ pub fn testnet_genesis(
|
|||||||
.collect(),
|
.collect(),
|
||||||
phantom: Default::default(),
|
phantom: Default::default(),
|
||||||
},
|
},
|
||||||
pallet_contracts: ContractsConfig {
|
|
||||||
// println should only be enabled on development chains
|
|
||||||
current_schedule: pallet_contracts::Schedule::default()
|
|
||||||
.enable_println(enable_println),
|
|
||||||
},
|
|
||||||
pallet_sudo: SudoConfig {
|
pallet_sudo: SudoConfig {
|
||||||
key: root_key,
|
key: root_key,
|
||||||
},
|
},
|
||||||
@@ -352,7 +346,6 @@ fn development_config_genesis() -> GenesisConfig {
|
|||||||
vec![],
|
vec![],
|
||||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||||
None,
|
None,
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +373,6 @@ fn local_testnet_genesis() -> GenesisConfig {
|
|||||||
vec![],
|
vec![],
|
||||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||||
None,
|
None,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +406,6 @@ pub(crate) mod tests {
|
|||||||
vec![],
|
vec![],
|
||||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||||
None,
|
None,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -793,7 +793,7 @@ parameter_types! {
|
|||||||
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -
|
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -
|
||||||
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)
|
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)
|
||||||
)) / 5) as u32;
|
)) / 5) as u32;
|
||||||
pub MaxCodeSize: u32 = 128 * 1024;
|
pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_contracts::Config for Runtime {
|
impl pallet_contracts::Config for Runtime {
|
||||||
@@ -810,13 +810,12 @@ impl pallet_contracts::Config for Runtime {
|
|||||||
type RentFraction = RentFraction;
|
type RentFraction = RentFraction;
|
||||||
type SurchargeReward = SurchargeReward;
|
type SurchargeReward = SurchargeReward;
|
||||||
type CallStack = [pallet_contracts::Frame<Self>; 31];
|
type CallStack = [pallet_contracts::Frame<Self>; 31];
|
||||||
type MaxValueSize = MaxValueSize;
|
|
||||||
type WeightPrice = pallet_transaction_payment::Module<Self>;
|
type WeightPrice = pallet_transaction_payment::Module<Self>;
|
||||||
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
|
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
|
||||||
type ChainExtension = ();
|
type ChainExtension = ();
|
||||||
type DeletionQueueDepth = DeletionQueueDepth;
|
type DeletionQueueDepth = DeletionQueueDepth;
|
||||||
type DeletionWeightLimit = DeletionWeightLimit;
|
type DeletionWeightLimit = DeletionWeightLimit;
|
||||||
type MaxCodeSize = MaxCodeSize;
|
type Schedule = Schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_sudo::Config for Runtime {
|
impl pallet_sudo::Config for Runtime {
|
||||||
@@ -1114,7 +1113,7 @@ construct_runtime!(
|
|||||||
TechnicalMembership: pallet_membership::<Instance1>::{Pallet, Call, Storage, Event<T>, Config<T>},
|
TechnicalMembership: pallet_membership::<Instance1>::{Pallet, Call, Storage, Event<T>, Config<T>},
|
||||||
Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned},
|
Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned},
|
||||||
Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>},
|
Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>},
|
||||||
Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},
|
Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>},
|
||||||
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>},
|
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>},
|
||||||
ImOnline: pallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
|
ImOnline: pallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
|
||||||
AuthorityDiscovery: pallet_authority_discovery::{Pallet, Call, Config},
|
AuthorityDiscovery: pallet_authority_discovery::{Pallet, Call, Config},
|
||||||
@@ -1354,7 +1353,7 @@ impl_runtime_apis! {
|
|||||||
gas_limit: u64,
|
gas_limit: u64,
|
||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
) -> pallet_contracts_primitives::ContractExecResult {
|
) -> pallet_contracts_primitives::ContractExecResult {
|
||||||
Contracts::bare_call(origin, dest, value, gas_limit, input_data)
|
Contracts::bare_call(origin, dest, value, gas_limit, input_data, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate(
|
fn instantiate(
|
||||||
@@ -1366,7 +1365,7 @@ impl_runtime_apis! {
|
|||||||
salt: Vec<u8>,
|
salt: Vec<u8>,
|
||||||
) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>
|
) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>
|
||||||
{
|
{
|
||||||
Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true)
|
Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_storage(
|
fn get_storage(
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use crate::keyring::*;
|
|||||||
use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
||||||
use node_runtime::{
|
use node_runtime::{
|
||||||
GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, SystemConfig,
|
GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, SystemConfig,
|
||||||
GrandpaConfig, IndicesConfig, ContractsConfig, SocietyConfig, wasm_binary_unwrap,
|
GrandpaConfig, IndicesConfig, SocietyConfig, wasm_binary_unwrap,
|
||||||
AccountId, StakerStatus, BabeConfig, BABE_GENESIS_EPOCH_CONFIG,
|
AccountId, StakerStatus, BabeConfig, BABE_GENESIS_EPOCH_CONFIG,
|
||||||
};
|
};
|
||||||
use node_runtime::constants::currency::*;
|
use node_runtime::constants::currency::*;
|
||||||
@@ -97,9 +97,6 @@ pub fn config_endowed(
|
|||||||
invulnerables: vec![alice(), bob(), charlie()],
|
invulnerables: vec![alice(), bob(), charlie()],
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
},
|
},
|
||||||
pallet_contracts: ContractsConfig {
|
|
||||||
current_schedule: Default::default(),
|
|
||||||
},
|
|
||||||
pallet_babe: BabeConfig {
|
pallet_babe: BabeConfig {
|
||||||
authorities: vec![],
|
authorities: vec![],
|
||||||
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
|
epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG),
|
||||||
|
|||||||
@@ -106,14 +106,11 @@ fn genesis_constructor(
|
|||||||
.map(chain_spec::authority_keys_from_seed)
|
.map(chain_spec::authority_keys_from_seed)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let enable_println = true;
|
|
||||||
|
|
||||||
chain_spec::testnet_genesis(
|
chain_spec::testnet_genesis(
|
||||||
authorities,
|
authorities,
|
||||||
nominator_accounts.to_vec(),
|
nominator_accounts.to_vec(),
|
||||||
sudo_account.clone(),
|
sudo_account.clone(),
|
||||||
Some(endowed_accounts.to_vec()),
|
Some(endowed_accounts.to_vec()),
|
||||||
enable_println,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ In other words: Upgrading this pallet will not break pre-existing contracts.
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Replaced `seal_println` with `seal_debug_message` which allows output to an RPC client.
|
||||||
|
[1](https://github.com/paritytech/substrate/pull/8773)
|
||||||
|
|
||||||
- Add new `instantiate` RPC that allows clients to dry-run contract instantiation.
|
- Add new `instantiate` RPC that allows clients to dry-run contract instantiation.
|
||||||
|
[1](https://github.com/paritytech/substrate/pull/8451)
|
||||||
|
|
||||||
- Make storage and fields of `Schedule` private to the crate.
|
- Make storage and fields of `Schedule` private to the crate.
|
||||||
[1](https://github.com/paritytech/substrate/pull/8359)
|
[1](https://github.com/paritytech/substrate/pull/8359)
|
||||||
|
|||||||
@@ -59,17 +59,26 @@ writing WebAssembly based smart contracts in the Rust programming language.
|
|||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
Contracts can emit messages to the node console when run on a development chain through the
|
Contracts can emit messages to the client when called as RPC through the `seal_debug_message`
|
||||||
`seal_println` API. This is exposed in ink! via
|
API. This is exposed in ink! via
|
||||||
[`ink_env::debug_println()`](https://docs.rs/ink_env/latest/ink_env/fn.debug_println.html).
|
[`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
|
Those messages are gathered into an internal buffer and send to the RPC client.
|
||||||
to at least the `info` level which is the default. However, those messages are easy to overlook
|
It is up the the individual client if and how those messages are presented to the user.
|
||||||
because of the noise generated by block production. A good starting point for contract debugging
|
|
||||||
could be:
|
This buffer is also printed as a debug message. In order to see these messages on the node
|
||||||
|
console the log level for the `runtime::contracts` target needs to be raised to at least
|
||||||
|
the `debug` level. However, those messages are easy to overlook because of the noise generated
|
||||||
|
by block production. A good starting point for observing them on the console is:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --release -- --dev --tmp -lerror,runtime::contracts
|
cargo run --release -- --dev --tmp -lerror,runtime::contracts=debug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This raises the log level of `runtime::contracts` to `debug` and all other targets
|
||||||
|
to `error` in order to prevent them from spamming the console.
|
||||||
|
|
||||||
|
`--dev`: Use a dev chain spec
|
||||||
|
`--tmp`: Use temporary storage for chain data (the chain state is deleted on exit)
|
||||||
|
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
|
|||||||
@@ -37,18 +37,22 @@ use serde::{Serialize, Deserialize};
|
|||||||
pub struct ContractResult<T> {
|
pub struct ContractResult<T> {
|
||||||
/// How much gas was consumed during execution.
|
/// How much gas was consumed during execution.
|
||||||
pub gas_consumed: u64,
|
pub gas_consumed: u64,
|
||||||
/// An optional debug message. This message is only non-empty when explicitly requested
|
/// An optional debug message. This message is only filled when explicitly requested
|
||||||
/// by the code that calls into the contract.
|
/// by the code that calls into the contract. Otherwise it is empty.
|
||||||
///
|
///
|
||||||
/// The contained bytes are valid UTF-8. This is not declared as `String` because
|
/// 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
|
/// this type is not allowed within the runtime.
|
||||||
/// to present the message to its users.
|
///
|
||||||
|
/// Clients should not make any assumptions about the format of the buffer.
|
||||||
|
/// They should just display it as-is. It is **not** only a collection of log lines
|
||||||
|
/// provided by a contract but a formatted buffer with different sections.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The debug message is never generated during on-chain execution. It is reserved for
|
/// The debug message is never generated during on-chain execution. It is reserved for
|
||||||
/// RPC calls.
|
/// RPC calls.
|
||||||
pub debug_message: Bytes,
|
#[cfg_attr(feature = "std", serde(with = "as_string"))]
|
||||||
|
pub debug_message: Vec<u8>,
|
||||||
/// The execution result of the wasm code.
|
/// The execution result of the wasm code.
|
||||||
pub result: T,
|
pub result: T,
|
||||||
}
|
}
|
||||||
@@ -146,3 +150,19 @@ pub enum Code<Hash> {
|
|||||||
/// The code hash of an on-chain wasm blob.
|
/// The code hash of an on-chain wasm blob.
|
||||||
Existing(Hash),
|
Existing(Hash),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod as_string {
|
||||||
|
use super::*;
|
||||||
|
use serde::{Serializer, Deserializer, ser::Error};
|
||||||
|
|
||||||
|
pub fn serialize<S: Serializer>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
std::str::from_utf8(bytes)
|
||||||
|
.map_err(|e| S::Error::custom(format!("Debug buffer contains invalid UTF8: {}", e)))?
|
||||||
|
.serialize(serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
|
||||||
|
Ok(String::deserialize(deserializer)?.into_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
(import "seal0" "seal_instantiate" (func $seal_instantiate
|
(import "seal0" "seal_instantiate" (func $seal_instantiate
|
||||||
(param i32 i32 i64 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32)
|
(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))
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
(func $assert (param i32)
|
(func $assert (param i32)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
;; Emit a "Hello World!" debug message
|
||||||
|
(module
|
||||||
|
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(data (i32.const 0) "\fc")
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
(call $seal_debug_message
|
||||||
|
(i32.const 0) ;; Pointer to the text buffer
|
||||||
|
(i32.const 12) ;; The size of the buffer
|
||||||
|
)
|
||||||
|
;; the above call traps because we supplied invalid utf8
|
||||||
|
unreachable
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "deploy"))
|
||||||
|
)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
;; Emit a "Hello World!" debug message but assume that logging is disabled.
|
||||||
|
(module
|
||||||
|
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(data (i32.const 0) "Hello World!")
|
||||||
|
|
||||||
|
(func $assert_eq (param i32 i32)
|
||||||
|
(block $ok
|
||||||
|
(br_if $ok
|
||||||
|
(i32.eq (get_local 0) (get_local 1))
|
||||||
|
)
|
||||||
|
(unreachable)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
(call $assert_eq
|
||||||
|
(call $seal_debug_message
|
||||||
|
(i32.const 0) ;; Pointer to the text buffer
|
||||||
|
(i32.const 12) ;; The size of the buffer
|
||||||
|
)
|
||||||
|
(i32.const 9) ;; LoggingDisabled return code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "deploy"))
|
||||||
|
)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
;; Emit a "Hello World!" debug message
|
||||||
|
(module
|
||||||
|
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(data (i32.const 0) "Hello World!")
|
||||||
|
|
||||||
|
(func $assert_eq (param i32 i32)
|
||||||
|
(block $ok
|
||||||
|
(br_if $ok
|
||||||
|
(i32.eq (get_local 0) (get_local 1))
|
||||||
|
)
|
||||||
|
(unreachable)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
(call $assert_eq
|
||||||
|
(call $seal_debug_message
|
||||||
|
(i32.const 0) ;; Pointer to the text buffer
|
||||||
|
(i32.const 12) ;; The size of the buffer
|
||||||
|
)
|
||||||
|
(i32.const 0) ;; success return code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "deploy"))
|
||||||
|
)
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
//! we define this simple definition of a contract that can be passed to `create_code` that
|
//! 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.
|
//! compiles it down into a `WasmModule` that can be used as a contract's code.
|
||||||
|
|
||||||
use crate::{Config, CurrentSchedule};
|
use crate::Config;
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
|
Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
|
||||||
};
|
};
|
||||||
@@ -33,6 +33,7 @@ use sp_core::crypto::UncheckedFrom;
|
|||||||
use sp_runtime::traits::Hash;
|
use sp_runtime::traits::Hash;
|
||||||
use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
|
use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
|
||||||
use sp_std::{prelude::*, convert::TryFrom, borrow::ToOwned};
|
use sp_std::{prelude::*, convert::TryFrom, borrow::ToOwned};
|
||||||
|
use frame_support::traits::Get;
|
||||||
|
|
||||||
/// Pass to `create_code` in order to create a compiled `WasmModule`.
|
/// Pass to `create_code` in order to create a compiled `WasmModule`.
|
||||||
///
|
///
|
||||||
@@ -223,7 +224,7 @@ where
|
|||||||
if def.inject_stack_metering {
|
if def.inject_stack_metering {
|
||||||
code = inject_limiter(
|
code = inject_limiter(
|
||||||
code,
|
code,
|
||||||
<CurrentSchedule<T>>::get().limits.stack_height
|
T::Schedule::get().limits.stack_height
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@@ -503,5 +504,5 @@ where
|
|||||||
T: Config,
|
T: Config,
|
||||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
<CurrentSchedule<T>>::get().limits.memory_pages
|
T::Schedule::get().limits.memory_pages
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ benchmarks! {
|
|||||||
|
|
||||||
on_initialize_per_trie_key {
|
on_initialize_per_trie_key {
|
||||||
let k in 0..1024;
|
let k in 0..1024;
|
||||||
let instance = ContractWithStorage::<T>::new(k, T::MaxValueSize::get())?;
|
let instance = ContractWithStorage::<T>::new(k, T::Schedule::get().limits.payload_len)?;
|
||||||
Storage::<T>::queue_trie_for_deletion(&instance.contract.alive_info()?)?;
|
Storage::<T>::queue_trie_for_deletion(&instance.contract.alive_info()?)?;
|
||||||
}: {
|
}: {
|
||||||
Storage::<T>::process_deletion_queue_batch(Weight::max_value())
|
Storage::<T>::process_deletion_queue_batch(Weight::max_value())
|
||||||
@@ -311,23 +311,15 @@ benchmarks! {
|
|||||||
// first time after a new schedule was deployed: For every new schedule a contract needs
|
// first time after a new schedule was deployed: For every new schedule a contract needs
|
||||||
// to re-run the instrumentation once.
|
// to re-run the instrumentation once.
|
||||||
instrument {
|
instrument {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
|
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
|
||||||
Contracts::<T>::store_code_raw(code)?;
|
Contracts::<T>::store_code_raw(code)?;
|
||||||
let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
|
let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
}: {
|
}: {
|
||||||
Contracts::<T>::reinstrument_module(&mut module, &schedule)?;
|
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.
|
// This constructs a contract that is maximal expensive to instrument.
|
||||||
// It creates a maximum number of metering blocks per byte.
|
// 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
|
// The size of the salt influences the runtime because is is hashed in order to
|
||||||
@@ -340,7 +332,7 @@ benchmarks! {
|
|||||||
// We cannot let `c` grow to the maximum code size because the code is not allowed
|
// 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**.
|
// to be larger than the maximum size **after instrumentation**.
|
||||||
instantiate_with_code {
|
instantiate_with_code {
|
||||||
let c in 0 .. Perbill::from_percent(50).mul_ceil(T::MaxCodeSize::get() / 1024);
|
let c in 0 .. Perbill::from_percent(50).mul_ceil(T::Schedule::get().limits.code_len / 1024);
|
||||||
let s in 0 .. code::max_pages::<T>() * 64;
|
let s in 0 .. code::max_pages::<T>() * 64;
|
||||||
let salt = vec![42u8; (s * 1024) as usize];
|
let salt = vec![42u8; (s * 1024) as usize];
|
||||||
let endowment = caller_funding::<T>() / 3u32.into();
|
let endowment = caller_funding::<T>() / 3u32.into();
|
||||||
@@ -363,7 +355,7 @@ benchmarks! {
|
|||||||
// `c`: Size of the code in kilobytes.
|
// `c`: Size of the code in kilobytes.
|
||||||
// `s`: Size of the salt in kilobytes.
|
// `s`: Size of the salt in kilobytes.
|
||||||
instantiate {
|
instantiate {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let s in 0 .. code::max_pages::<T>() * 64;
|
let s in 0 .. code::max_pages::<T>() * 64;
|
||||||
let salt = vec![42u8; (s * 1024) as usize];
|
let salt = vec![42u8; (s * 1024) as usize];
|
||||||
let endowment = caller_funding::<T>() / 3u32.into();
|
let endowment = caller_funding::<T>() / 3u32.into();
|
||||||
@@ -390,7 +382,7 @@ benchmarks! {
|
|||||||
// part of `seal_input`.
|
// part of `seal_input`.
|
||||||
// `c`: Size of the code in kilobytes.
|
// `c`: Size of the code in kilobytes.
|
||||||
call {
|
call {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let data = vec![42u8; 1024];
|
let data = vec![42u8; 1024];
|
||||||
let instance = Contract::<T>::with_caller(
|
let instance = Contract::<T>::with_caller(
|
||||||
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
|
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
|
||||||
@@ -423,7 +415,7 @@ benchmarks! {
|
|||||||
// the reward for removing them.
|
// the reward for removing them.
|
||||||
// `c`: Size of the code of the contract that should be evicted.
|
// `c`: Size of the code of the contract that should be evicted.
|
||||||
claim_surcharge {
|
claim_surcharge {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let instance = Contract::<T>::with_caller(
|
let instance = Contract::<T>::with_caller(
|
||||||
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
|
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
|
||||||
)?;
|
)?;
|
||||||
@@ -730,7 +722,7 @@ benchmarks! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
seal_terminate_per_code_kb {
|
seal_terminate_per_code_kb {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
|
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
|
||||||
let beneficiary_bytes = beneficiary.encode();
|
let beneficiary_bytes = beneficiary.encode();
|
||||||
let beneficiary_len = beneficiary_bytes.len();
|
let beneficiary_len = beneficiary_bytes.len();
|
||||||
@@ -771,7 +763,7 @@ benchmarks! {
|
|||||||
// Restore just moves the trie id from origin to destination and therefore
|
// 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
|
// 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.
|
// trigger any edge case we won't use an empty contract as destination.
|
||||||
let mut tombstone = ContractWithStorage::<T>::new(10, T::MaxValueSize::get())?;
|
let mut tombstone = ContractWithStorage::<T>::new(10, T::Schedule::get().limits.payload_len)?;
|
||||||
tombstone.evict()?;
|
tombstone.evict()?;
|
||||||
|
|
||||||
let dest = tombstone.contract.account_id.encode();
|
let dest = tombstone.contract.account_id.encode();
|
||||||
@@ -847,14 +839,14 @@ benchmarks! {
|
|||||||
// `t`: Code size of tombstone contract
|
// `t`: Code size of tombstone contract
|
||||||
// `d`: Number of supplied delta keys
|
// `d`: Number of supplied delta keys
|
||||||
seal_restore_to_per_code_kb_delta {
|
seal_restore_to_per_code_kb_delta {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let t in 0 .. T::MaxCodeSize::get() / 1024;
|
let t in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let d in 0 .. API_BENCHMARK_BATCHES;
|
let d in 0 .. API_BENCHMARK_BATCHES;
|
||||||
let mut tombstone = ContractWithStorage::<T>::with_code(
|
let mut tombstone = ContractWithStorage::<T>::with_code(
|
||||||
WasmModule::<T>::dummy_with_bytes(t * 1024), 0, 0
|
WasmModule::<T>::dummy_with_bytes(t * 1024), 0, 0
|
||||||
)?;
|
)?;
|
||||||
tombstone.evict()?;
|
tombstone.evict()?;
|
||||||
let delta = create_storage::<T>(d * API_BENCHMARK_BATCH_SIZE, T::MaxValueSize::get())?;
|
let delta = create_storage::<T>(d * API_BENCHMARK_BATCH_SIZE, T::Schedule::get().limits.payload_len)?;
|
||||||
|
|
||||||
let dest = tombstone.contract.account_id.encode();
|
let dest = tombstone.contract.account_id.encode();
|
||||||
let dest_len = dest.len();
|
let dest_len = dest.len();
|
||||||
@@ -938,7 +930,7 @@ benchmarks! {
|
|||||||
seal_random {
|
seal_random {
|
||||||
let r in 0 .. API_BENCHMARK_BATCHES;
|
let r in 0 .. API_BENCHMARK_BATCHES;
|
||||||
let pages = code::max_pages::<T>();
|
let pages = code::max_pages::<T>();
|
||||||
let subject_len = <CurrentSchedule<T>>::get().limits.subject_len;
|
let subject_len = T::Schedule::get().limits.subject_len;
|
||||||
assert!(subject_len < 1024);
|
assert!(subject_len < 1024);
|
||||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||||
memory: Some(ImportedMemory::max::<T>()),
|
memory: Some(ImportedMemory::max::<T>()),
|
||||||
@@ -994,8 +986,8 @@ benchmarks! {
|
|||||||
// `t`: Number of topics
|
// `t`: Number of topics
|
||||||
// `n`: Size of event payload in kb
|
// `n`: Size of event payload in kb
|
||||||
seal_deposit_event_per_topic_and_kb {
|
seal_deposit_event_per_topic_and_kb {
|
||||||
let t in 0 .. <CurrentSchedule<T>>::get().limits.event_topics;
|
let t in 0 .. T::Schedule::get().limits.event_topics;
|
||||||
let n in 0 .. T::MaxValueSize::get() / 1024;
|
let n in 0 .. T::Schedule::get().limits.payload_len / 1024;
|
||||||
let mut topics = (0..API_BENCHMARK_BATCH_SIZE)
|
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())
|
.map(|n| (n * t..n * t + t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode())
|
||||||
.peekable();
|
.peekable();
|
||||||
@@ -1055,6 +1047,31 @@ benchmarks! {
|
|||||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||||
|
|
||||||
|
// The size of the supplied message does not influence the weight because as it is never
|
||||||
|
// processed during on-chain execution: It is only ever read during debugging which happens
|
||||||
|
// when the contract is called as RPC where weights do not matter.
|
||||||
|
seal_debug_message {
|
||||||
|
let r in 0 .. API_BENCHMARK_BATCHES;
|
||||||
|
let max_bytes = code::max_pages::<T>() * 64 * 1024;
|
||||||
|
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||||
|
memory: Some(ImportedMemory { min_pages: 1, max_pages: 1 }),
|
||||||
|
imported_functions: vec![ImportedFunction {
|
||||||
|
name: "seal_debug_message",
|
||||||
|
params: vec![ValueType::I32, ValueType::I32],
|
||||||
|
return_type: Some(ValueType::I32),
|
||||||
|
}],
|
||||||
|
call_body: Some(body::repeated(r * API_BENCHMARK_BATCH_SIZE, &[
|
||||||
|
Instruction::I32Const(0), // value_ptr
|
||||||
|
Instruction::I32Const(max_bytes as i32), // value_len
|
||||||
|
Instruction::Call(0),
|
||||||
|
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![])
|
||||||
|
|
||||||
// Only the overhead of calling the function itself with minimal arguments.
|
// 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
|
// 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
|
// to generate unique storage accesses. However, it is still dominated by the storage
|
||||||
@@ -1091,7 +1108,7 @@ benchmarks! {
|
|||||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||||
|
|
||||||
seal_set_storage_per_kb {
|
seal_set_storage_per_kb {
|
||||||
let n in 0 .. T::MaxValueSize::get() / 1024;
|
let n in 0 .. T::Schedule::get().limits.payload_len / 1024;
|
||||||
let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
|
let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
|
||||||
let key_len = key.len();
|
let key_len = key.len();
|
||||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||||
@@ -1155,7 +1172,7 @@ benchmarks! {
|
|||||||
<System<T>>::block_number(),
|
<System<T>>::block_number(),
|
||||||
&mut info,
|
&mut info,
|
||||||
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
|
key.as_slice().try_into().map_err(|e| "Key has wrong length")?,
|
||||||
Some(vec![42; T::MaxValueSize::get() as usize])
|
Some(vec![42; T::Schedule::get().limits.payload_len as usize])
|
||||||
)
|
)
|
||||||
.map_err(|_| "Failed to write to storage during setup.")?;
|
.map_err(|_| "Failed to write to storage during setup.")?;
|
||||||
}
|
}
|
||||||
@@ -1210,7 +1227,7 @@ benchmarks! {
|
|||||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||||
|
|
||||||
seal_get_storage_per_kb {
|
seal_get_storage_per_kb {
|
||||||
let n in 0 .. T::MaxValueSize::get() / 1024;
|
let n in 0 .. T::Schedule::get().limits.payload_len / 1024;
|
||||||
let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
|
let key = T::Hashing::hash_of(&1u32).as_ref().to_vec();
|
||||||
let key_len = key.len();
|
let key_len = key.len();
|
||||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||||
@@ -1227,7 +1244,7 @@ benchmarks! {
|
|||||||
},
|
},
|
||||||
DataSegment {
|
DataSegment {
|
||||||
offset: key_len as u32,
|
offset: key_len as u32,
|
||||||
value: T::MaxValueSize::get().to_le_bytes().into(),
|
value: T::Schedule::get().limits.payload_len.to_le_bytes().into(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
call_body: Some(body::repeated(API_BENCHMARK_BATCH_SIZE, &[
|
call_body: Some(body::repeated(API_BENCHMARK_BATCH_SIZE, &[
|
||||||
@@ -1363,7 +1380,7 @@ benchmarks! {
|
|||||||
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
|
||||||
|
|
||||||
seal_call_per_code_transfer_input_output_kb {
|
seal_call_per_code_transfer_input_output_kb {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let t in 0 .. 1;
|
let t in 0 .. 1;
|
||||||
let i in 0 .. code::max_pages::<T>() * 64;
|
let i in 0 .. code::max_pages::<T>() * 64;
|
||||||
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
|
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||||
@@ -1560,7 +1577,7 @@ benchmarks! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
seal_instantiate_per_code_input_output_salt_kb {
|
seal_instantiate_per_code_input_output_salt_kb {
|
||||||
let c in 0 .. T::MaxCodeSize::get() / 1024;
|
let c in 0 .. T::Schedule::get().limits.code_len / 1024;
|
||||||
let i in 0 .. (code::max_pages::<T>() - 1) * 64;
|
let i in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||||
let o 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 s in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||||
@@ -1927,7 +1944,7 @@ benchmarks! {
|
|||||||
|
|
||||||
// w_br_table_per_entry = w_bench
|
// w_br_table_per_entry = w_bench
|
||||||
instr_br_table_per_entry {
|
instr_br_table_per_entry {
|
||||||
let e in 1 .. <CurrentSchedule<T>>::get().limits.br_table_size;
|
let e in 1 .. T::Schedule::get().limits.br_table_size;
|
||||||
let entry: Vec<u32> = [0, 1].iter()
|
let entry: Vec<u32> = [0, 1].iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.cycle()
|
.cycle()
|
||||||
@@ -1983,7 +2000,7 @@ benchmarks! {
|
|||||||
// w_call_indrect = w_bench - 3 * w_param
|
// w_call_indrect = w_bench - 3 * w_param
|
||||||
instr_call_indirect {
|
instr_call_indirect {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
|
let num_elements = T::Schedule::get().limits.table_size;
|
||||||
use self::code::TableSegment;
|
use self::code::TableSegment;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
// We need to make use of the stack here in order to trigger stack height
|
// We need to make use of the stack here in order to trigger stack height
|
||||||
@@ -2013,8 +2030,8 @@ benchmarks! {
|
|||||||
// linearly depend on the amount of parameters to this function.
|
// linearly depend on the amount of parameters to this function.
|
||||||
// Please note that this is not necessary with a direct call.
|
// Please note that this is not necessary with a direct call.
|
||||||
instr_call_indirect_per_param {
|
instr_call_indirect_per_param {
|
||||||
let p in 0 .. <CurrentSchedule<T>>::get().limits.parameters;
|
let p in 0 .. T::Schedule::get().limits.parameters;
|
||||||
let num_elements = <CurrentSchedule<T>>::get().limits.table_size;
|
let num_elements = T::Schedule::get().limits.table_size;
|
||||||
use self::code::TableSegment;
|
use self::code::TableSegment;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
// We need to make use of the stack here in order to trigger stack height
|
// We need to make use of the stack here in order to trigger stack height
|
||||||
@@ -2044,7 +2061,7 @@ benchmarks! {
|
|||||||
// w_local_get = w_bench - 1 * w_param
|
// w_local_get = w_bench - 1 * w_param
|
||||||
instr_local_get {
|
instr_local_get {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
|
let max_locals = T::Schedule::get().limits.stack_height;
|
||||||
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
||||||
RandomGetLocal(0, max_locals),
|
RandomGetLocal(0, max_locals),
|
||||||
Regular(Instruction::Drop),
|
Regular(Instruction::Drop),
|
||||||
@@ -2061,7 +2078,7 @@ benchmarks! {
|
|||||||
// w_local_set = w_bench - 1 * w_param
|
// w_local_set = w_bench - 1 * w_param
|
||||||
instr_local_set {
|
instr_local_set {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
|
let max_locals = T::Schedule::get().limits.stack_height;
|
||||||
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
||||||
RandomI64Repeated(1),
|
RandomI64Repeated(1),
|
||||||
RandomSetLocal(0, max_locals),
|
RandomSetLocal(0, max_locals),
|
||||||
@@ -2078,7 +2095,7 @@ benchmarks! {
|
|||||||
// w_local_tee = w_bench - 2 * w_param
|
// w_local_tee = w_bench - 2 * w_param
|
||||||
instr_local_tee {
|
instr_local_tee {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_locals = <CurrentSchedule<T>>::get().limits.stack_height;
|
let max_locals = T::Schedule::get().limits.stack_height;
|
||||||
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
let mut call_body = body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
||||||
RandomI64Repeated(1),
|
RandomI64Repeated(1),
|
||||||
RandomTeeLocal(0, max_locals),
|
RandomTeeLocal(0, max_locals),
|
||||||
@@ -2096,7 +2113,7 @@ benchmarks! {
|
|||||||
// w_global_get = w_bench - 1 * w_param
|
// w_global_get = w_bench - 1 * w_param
|
||||||
instr_global_get {
|
instr_global_get {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_globals = <CurrentSchedule<T>>::get().limits.globals;
|
let max_globals = T::Schedule::get().limits.globals;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
||||||
RandomGetGlobal(0, max_globals),
|
RandomGetGlobal(0, max_globals),
|
||||||
@@ -2112,7 +2129,7 @@ benchmarks! {
|
|||||||
// w_global_set = w_bench - 1 * w_param
|
// w_global_set = w_bench - 1 * w_param
|
||||||
instr_global_set {
|
instr_global_set {
|
||||||
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
let r in 0 .. INSTR_BENCHMARK_BATCHES;
|
||||||
let max_globals = <CurrentSchedule<T>>::get().limits.globals;
|
let max_globals = T::Schedule::get().limits.globals;
|
||||||
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
|
||||||
call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
call_body: Some(body::repeated_dyn(r * INSTR_BENCHMARK_BATCH_SIZE, vec![
|
||||||
RandomI64Repeated(1),
|
RandomI64Repeated(1),
|
||||||
|
|||||||
@@ -315,6 +315,16 @@ pub trait Ext: sealing::Sealed {
|
|||||||
|
|
||||||
/// Get a mutable reference to the nested gas meter.
|
/// Get a mutable reference to the nested gas meter.
|
||||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T>;
|
fn gas_meter(&mut self) -> &mut GasMeter<Self::T>;
|
||||||
|
|
||||||
|
/// Append a string to the debug buffer.
|
||||||
|
///
|
||||||
|
/// It is added as-is without any additional new line.
|
||||||
|
///
|
||||||
|
/// This is a no-op if debug message recording is disabled which is always the case
|
||||||
|
/// when the code is executing on-chain.
|
||||||
|
///
|
||||||
|
/// Returns `true` if debug message recording is enabled. Otherwise `false` is returned.
|
||||||
|
fn append_debug_buffer(&mut self, msg: &str) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the different functions that can be exported by an [`Executable`].
|
/// Describes the different functions that can be exported by an [`Executable`].
|
||||||
@@ -434,6 +444,11 @@ pub struct Stack<'a, T: Config, E> {
|
|||||||
frames: SmallVec<T::CallStack>,
|
frames: SmallVec<T::CallStack>,
|
||||||
/// Statically guarantee that each call stack has at least one frame.
|
/// Statically guarantee that each call stack has at least one frame.
|
||||||
first_frame: Frame<T>,
|
first_frame: Frame<T>,
|
||||||
|
/// A text buffer used to output human readable information.
|
||||||
|
///
|
||||||
|
/// All the bytes added to this field should be valid UTF-8. The buffer has no defined
|
||||||
|
/// structure and is intended to be shown to users as-is for debugging purposes.
|
||||||
|
debug_message: Option<&'a mut Vec<u8>>,
|
||||||
/// No executable is held by the struct but influences its behaviour.
|
/// No executable is held by the struct but influences its behaviour.
|
||||||
_phantom: PhantomData<E>,
|
_phantom: PhantomData<E>,
|
||||||
}
|
}
|
||||||
@@ -442,6 +457,11 @@ pub struct Stack<'a, T: Config, E> {
|
|||||||
///
|
///
|
||||||
/// For each nested contract call or instantiate one frame is created. It holds specific
|
/// For each nested contract call or instantiate one frame is created. It holds specific
|
||||||
/// information for the said call and caches the in-storage `ContractInfo` data structure.
|
/// information for the said call and caches the in-storage `ContractInfo` data structure.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This is an internal data structure. It is exposed to the public for the sole reason
|
||||||
|
/// of specifying [`Config::CallStack`].
|
||||||
pub struct Frame<T: Config> {
|
pub struct Frame<T: Config> {
|
||||||
/// The account id of the executing contract.
|
/// The account id of the executing contract.
|
||||||
account_id: T::AccountId,
|
account_id: T::AccountId,
|
||||||
@@ -574,6 +594,11 @@ where
|
|||||||
{
|
{
|
||||||
/// Create an run a new call stack by calling into `dest`.
|
/// Create an run a new call stack by calling into `dest`.
|
||||||
///
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// `debug_message` should only ever be set to `Some` when executing as an RPC because
|
||||||
|
/// it adds allocations and could be abused to drive the runtime into an OOM panic.
|
||||||
|
///
|
||||||
/// # Return Value
|
/// # Return Value
|
||||||
///
|
///
|
||||||
/// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
|
/// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
|
||||||
@@ -584,6 +609,7 @@ where
|
|||||||
schedule: &'a Schedule<T>,
|
schedule: &'a Schedule<T>,
|
||||||
value: BalanceOf<T>,
|
value: BalanceOf<T>,
|
||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
|
debug_message: Option<&'a mut Vec<u8>>,
|
||||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||||
let (mut stack, executable) = Self::new(
|
let (mut stack, executable) = Self::new(
|
||||||
FrameArgs::Call{dest, cached_info: None},
|
FrameArgs::Call{dest, cached_info: None},
|
||||||
@@ -591,12 +617,18 @@ where
|
|||||||
gas_meter,
|
gas_meter,
|
||||||
schedule,
|
schedule,
|
||||||
value,
|
value,
|
||||||
|
debug_message,
|
||||||
)?;
|
)?;
|
||||||
stack.run(executable, input_data)
|
stack.run(executable, input_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and run a new call stack by instantiating a new contract.
|
/// Create and run a new call stack by instantiating a new contract.
|
||||||
///
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// `debug_message` should only ever be set to `Some` when executing as an RPC because
|
||||||
|
/// it adds allocations and could be abused to drive the runtime into an OOM panic.
|
||||||
|
///
|
||||||
/// # Return Value
|
/// # Return Value
|
||||||
///
|
///
|
||||||
/// Result<(NewContractAccountId, ExecReturnValue), ExecError)>
|
/// Result<(NewContractAccountId, ExecReturnValue), ExecError)>
|
||||||
@@ -608,6 +640,7 @@ where
|
|||||||
value: BalanceOf<T>,
|
value: BalanceOf<T>,
|
||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
salt: &[u8],
|
salt: &[u8],
|
||||||
|
debug_message: Option<&'a mut Vec<u8>>,
|
||||||
) -> Result<(T::AccountId, ExecReturnValue), ExecError> {
|
) -> Result<(T::AccountId, ExecReturnValue), ExecError> {
|
||||||
let (mut stack, executable) = Self::new(
|
let (mut stack, executable) = Self::new(
|
||||||
FrameArgs::Instantiate {
|
FrameArgs::Instantiate {
|
||||||
@@ -620,6 +653,7 @@ where
|
|||||||
gas_meter,
|
gas_meter,
|
||||||
schedule,
|
schedule,
|
||||||
value,
|
value,
|
||||||
|
debug_message,
|
||||||
).map_err(|(e, _code_len)| e)?;
|
).map_err(|(e, _code_len)| e)?;
|
||||||
let account_id = stack.top_frame().account_id.clone();
|
let account_id = stack.top_frame().account_id.clone();
|
||||||
stack.run(executable, input_data)
|
stack.run(executable, input_data)
|
||||||
@@ -634,6 +668,7 @@ where
|
|||||||
gas_meter: &'a mut GasMeter<T>,
|
gas_meter: &'a mut GasMeter<T>,
|
||||||
schedule: &'a Schedule<T>,
|
schedule: &'a Schedule<T>,
|
||||||
value: BalanceOf<T>,
|
value: BalanceOf<T>,
|
||||||
|
debug_message: Option<&'a mut Vec<u8>>,
|
||||||
) -> Result<(Self, E), (ExecError, u32)> {
|
) -> Result<(Self, E), (ExecError, u32)> {
|
||||||
let (first_frame, executable) = Self::new_frame(args, value, gas_meter, 0, &schedule)?;
|
let (first_frame, executable) = Self::new_frame(args, value, gas_meter, 0, &schedule)?;
|
||||||
let stack = Self {
|
let stack = Self {
|
||||||
@@ -645,6 +680,7 @@ where
|
|||||||
account_counter: None,
|
account_counter: None,
|
||||||
first_frame,
|
first_frame,
|
||||||
frames: Default::default(),
|
frames: Default::default(),
|
||||||
|
debug_message,
|
||||||
_phantom: Default::default(),
|
_phantom: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -841,6 +877,7 @@ where
|
|||||||
|
|
||||||
// Pop the current frame from the stack and return it in case it needs to interact
|
// Pop the current frame from the stack and return it in case it needs to interact
|
||||||
// with duplicates that might exist on the stack.
|
// with duplicates that might exist on the stack.
|
||||||
|
// A `None` means that we are returning from the `first_frame`.
|
||||||
let frame = self.frames.pop();
|
let frame = self.frames.pop();
|
||||||
|
|
||||||
if let Some(frame) = frame {
|
if let Some(frame) = frame {
|
||||||
@@ -872,6 +909,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if let Some(message) = &self.debug_message {
|
||||||
|
log::debug!(
|
||||||
|
target: "runtime::contracts",
|
||||||
|
"Debug Message: {}",
|
||||||
|
core::str::from_utf8(message).unwrap_or("<Invalid UTF8>"),
|
||||||
|
);
|
||||||
|
}
|
||||||
// Write back to the root gas meter.
|
// Write back to the root gas meter.
|
||||||
self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_meter));
|
self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_meter));
|
||||||
// Only gas counter changes are persisted in case of a failure.
|
// Only gas counter changes are persisted in case of a failure.
|
||||||
@@ -1181,7 +1225,7 @@ where
|
|||||||
fn block_number(&self) -> T::BlockNumber { self.block_number }
|
fn block_number(&self) -> T::BlockNumber { self.block_number }
|
||||||
|
|
||||||
fn max_value_size(&self) -> u32 {
|
fn max_value_size(&self) -> u32 {
|
||||||
T::MaxValueSize::get()
|
T::Schedule::get().limits.payload_len
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
|
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
|
||||||
@@ -1199,6 +1243,17 @@ where
|
|||||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
||||||
&mut self.top_frame_mut().nested_meter
|
&mut self.top_frame_mut().nested_meter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_debug_buffer(&mut self, msg: &str) -> bool {
|
||||||
|
if let Some(buffer) = &mut self.debug_message {
|
||||||
|
if !msg.is_empty() {
|
||||||
|
buffer.extend(msg.as_bytes());
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deposit_event<T: Config>(
|
fn deposit_event<T: Config>(
|
||||||
@@ -1241,7 +1296,7 @@ mod tests {
|
|||||||
test_utils::{place_contract, set_balance, get_balance},
|
test_utils::{place_contract, set_balance, get_balance},
|
||||||
},
|
},
|
||||||
exec::ExportedFunction::*,
|
exec::ExportedFunction::*,
|
||||||
Error, Weight, CurrentSchedule,
|
Error, Weight,
|
||||||
};
|
};
|
||||||
use sp_core::Bytes;
|
use sp_core::Bytes;
|
||||||
use sp_runtime::DispatchError;
|
use sp_runtime::DispatchError;
|
||||||
@@ -1436,12 +1491,12 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, exec_ch);
|
place_contract(&BOB, exec_ch);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
MockStack::run_call(
|
MockStack::run_call(
|
||||||
ALICE, BOB, &mut gas_meter, &schedule, value, vec![],
|
ALICE, BOB, &mut gas_meter, &schedule, value, vec![], None,
|
||||||
),
|
),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
@@ -1487,7 +1542,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
set_balance(&origin, 100);
|
set_balance(&origin, 100);
|
||||||
let balance = get_balance(&dest);
|
let balance = get_balance(&dest);
|
||||||
@@ -1499,6 +1554,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
55,
|
55,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert!(!output.0.is_success());
|
assert!(!output.0.is_success());
|
||||||
@@ -1548,7 +1604,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
|
|
||||||
let result = MockStack::run_call(
|
let result = MockStack::run_call(
|
||||||
@@ -1558,6 +1614,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
@@ -1578,8 +1635,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&dest, return_ch);
|
place_contract(&BOB, return_ch);
|
||||||
|
|
||||||
let result = MockStack::run_call(
|
let result = MockStack::run_call(
|
||||||
origin,
|
origin,
|
||||||
@@ -1588,6 +1645,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
@@ -1605,7 +1663,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via call.
|
// This one tests passing the input data into a contract via call.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, input_data_ch);
|
place_contract(&BOB, input_data_ch);
|
||||||
|
|
||||||
let result = MockStack::run_call(
|
let result = MockStack::run_call(
|
||||||
@@ -1615,6 +1673,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![1, 2, 3, 4],
|
vec![1, 2, 3, 4],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
});
|
});
|
||||||
@@ -1629,7 +1688,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via instantiate.
|
// This one tests passing the input data into a contract via instantiate.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -1646,6 +1705,7 @@ mod tests {
|
|||||||
subsistence * 3,
|
subsistence * 3,
|
||||||
vec![1, 2, 3, 4],
|
vec![1, 2, 3, 4],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
});
|
});
|
||||||
@@ -1683,7 +1743,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
set_balance(&BOB, 1);
|
set_balance(&BOB, 1);
|
||||||
place_contract(&BOB, recurse_ch);
|
place_contract(&BOB, recurse_ch);
|
||||||
|
|
||||||
@@ -1694,6 +1754,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
value,
|
value,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
@@ -1732,7 +1793,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&dest, bob_ch);
|
place_contract(&dest, bob_ch);
|
||||||
place_contract(&CHARLIE, charlie_ch);
|
place_contract(&CHARLIE, charlie_ch);
|
||||||
|
|
||||||
@@ -1743,6 +1804,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
@@ -1771,7 +1833,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, bob_ch);
|
place_contract(&BOB, bob_ch);
|
||||||
place_contract(&CHARLIE, charlie_ch);
|
place_contract(&CHARLIE, charlie_ch);
|
||||||
|
|
||||||
@@ -1782,6 +1844,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
@@ -1793,7 +1856,7 @@ mod tests {
|
|||||||
let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
|
let dummy_ch = MockLoader::insert(Constructor, |_, _| exec_success());
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
dummy_ch, &schedule, &mut gas_meter
|
dummy_ch, &schedule, &mut gas_meter
|
||||||
@@ -1808,6 +1871,7 @@ mod tests {
|
|||||||
0, // <- zero endowment
|
0, // <- zero endowment
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
Err(_)
|
Err(_)
|
||||||
);
|
);
|
||||||
@@ -1822,7 +1886,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
dummy_ch, &schedule, &mut gas_meter
|
dummy_ch, &schedule, &mut gas_meter
|
||||||
@@ -1838,6 +1902,7 @@ mod tests {
|
|||||||
100,
|
100,
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
Ok((address, ref output)) if output.data == Bytes(vec![80, 65, 83, 83]) => address
|
Ok((address, ref output)) if output.data == Bytes(vec![80, 65, 83, 83]) => address
|
||||||
);
|
);
|
||||||
@@ -1859,7 +1924,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
dummy_ch, &schedule, &mut gas_meter
|
dummy_ch, &schedule, &mut gas_meter
|
||||||
@@ -1875,6 +1940,7 @@ mod tests {
|
|||||||
100,
|
100,
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
Ok((address, ref output)) if output.data == Bytes(vec![70, 65, 73, 76]) => address
|
Ok((address, ref output)) if output.data == Bytes(vec![70, 65, 73, 76]) => address
|
||||||
);
|
);
|
||||||
@@ -1908,13 +1974,13 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);
|
set_balance(&ALICE, Contracts::<Test>::subsistence_threshold() * 100);
|
||||||
place_contract(&BOB, instantiator_ch);
|
place_contract(&BOB, instantiator_ch);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
MockStack::run_call(
|
MockStack::run_call(
|
||||||
ALICE, BOB, &mut GasMeter::<Test>::new(GAS_LIMIT), &schedule, 20, vec![],
|
ALICE, BOB, &mut GasMeter::<Test>::new(GAS_LIMIT), &schedule, 20, vec![], None,
|
||||||
),
|
),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
@@ -1958,14 +2024,14 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
ExtBuilder::default().existential_deposit(15).build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
set_balance(&ALICE, 1000);
|
set_balance(&ALICE, 1000);
|
||||||
set_balance(&BOB, 100);
|
set_balance(&BOB, 100);
|
||||||
place_contract(&BOB, instantiator_ch);
|
place_contract(&BOB, instantiator_ch);
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
MockStack::run_call(
|
MockStack::run_call(
|
||||||
ALICE, BOB, &mut GasMeter::<Test>::new(GAS_LIMIT), &schedule, 20, vec![],
|
ALICE, BOB, &mut GasMeter::<Test>::new(GAS_LIMIT), &schedule, 20, vec![], None,
|
||||||
),
|
),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
@@ -1987,7 +2053,7 @@ mod tests {
|
|||||||
.existential_deposit(15)
|
.existential_deposit(15)
|
||||||
.build()
|
.build()
|
||||||
.execute_with(|| {
|
.execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
terminate_ch, &schedule, &mut gas_meter
|
terminate_ch, &schedule, &mut gas_meter
|
||||||
@@ -2003,6 +2069,7 @@ mod tests {
|
|||||||
100,
|
100,
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
Err(Error::<Test>::TerminatedInConstructor.into())
|
Err(Error::<Test>::TerminatedInConstructor.into())
|
||||||
);
|
);
|
||||||
@@ -2027,7 +2094,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
rent_allowance_ch, &schedule, &mut gas_meter
|
rent_allowance_ch, &schedule, &mut gas_meter
|
||||||
@@ -2042,6 +2109,7 @@ mod tests {
|
|||||||
subsistence * 5,
|
subsistence * 5,
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
});
|
});
|
||||||
@@ -2060,7 +2128,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
set_balance(&ALICE, subsistence * 10);
|
set_balance(&ALICE, subsistence * 10);
|
||||||
place_contract(&BOB, code_hash);
|
place_contract(&BOB, code_hash);
|
||||||
@@ -2071,6 +2139,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2109,7 +2178,7 @@ mod tests {
|
|||||||
|
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
set_balance(&ALICE, subsistence * 100);
|
set_balance(&ALICE, subsistence * 100);
|
||||||
place_contract(&BOB, code_hash);
|
place_contract(&BOB, code_hash);
|
||||||
@@ -2120,6 +2189,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
subsistence * 50,
|
subsistence * 50,
|
||||||
vec![],
|
vec![],
|
||||||
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2156,7 +2226,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via call.
|
// This one tests passing the input data into a contract via call.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
place_contract(&BOB, code_bob);
|
place_contract(&BOB, code_bob);
|
||||||
place_contract(&CHARLIE, code_charlie);
|
place_contract(&CHARLIE, code_charlie);
|
||||||
|
|
||||||
@@ -2167,6 +2237,7 @@ mod tests {
|
|||||||
&schedule,
|
&schedule,
|
||||||
0,
|
0,
|
||||||
vec![0],
|
vec![0],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
});
|
});
|
||||||
@@ -2174,10 +2245,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn recursive_call_during_constructor_fails() {
|
fn recursive_call_during_constructor_fails() {
|
||||||
let code = MockLoader::insert(Constructor, |ctx, executable| {
|
let code = MockLoader::insert(Constructor, |ctx, _| {
|
||||||
let my_hash = <Contracts<Test>>::contract_address(&ALICE, &executable.code_hash, &[]);
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
ctx.ext.call(0, my_hash, 0, vec![]),
|
ctx.ext.call(0, ctx.ext.address().clone(), 0, vec![]),
|
||||||
Err((ExecError{error, ..}, _)) if error == <Error<Test>>::NotCallable.into()
|
Err((ExecError{error, ..}, _)) if error == <Error<Test>>::NotCallable.into()
|
||||||
);
|
);
|
||||||
exec_success()
|
exec_success()
|
||||||
@@ -2185,7 +2255,7 @@ mod tests {
|
|||||||
|
|
||||||
// This one tests passing the input data into a contract via instantiate.
|
// This one tests passing the input data into a contract via instantiate.
|
||||||
ExtBuilder::default().build().execute_with(|| {
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
let schedule = <CurrentSchedule<Test>>::get();
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
let subsistence = Contracts::<Test>::subsistence_threshold();
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
let executable = MockExecutable::from_storage(
|
let executable = MockExecutable::from_storage(
|
||||||
@@ -2202,8 +2272,70 @@ mod tests {
|
|||||||
subsistence * 3,
|
subsistence * 3,
|
||||||
vec![],
|
vec![],
|
||||||
&[],
|
&[],
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_matches!(result, Ok(_));
|
assert_matches!(result, Ok(_));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn printing_works() {
|
||||||
|
let code_hash = MockLoader::insert(Call, |ctx, _| {
|
||||||
|
ctx.ext.append_debug_buffer("This is a test");
|
||||||
|
ctx.ext.append_debug_buffer("More text");
|
||||||
|
exec_success()
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut debug_buffer = Vec::new();
|
||||||
|
|
||||||
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
|
set_balance(&ALICE, subsistence * 10);
|
||||||
|
place_contract(&BOB, code_hash);
|
||||||
|
MockStack::run_call(
|
||||||
|
ALICE,
|
||||||
|
BOB,
|
||||||
|
&mut gas_meter,
|
||||||
|
&schedule,
|
||||||
|
0,
|
||||||
|
vec![],
|
||||||
|
Some(&mut debug_buffer),
|
||||||
|
).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(&String::from_utf8(debug_buffer).unwrap(), "This is a testMore text");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn printing_works_on_fail() {
|
||||||
|
let code_hash = MockLoader::insert(Call, |ctx, _| {
|
||||||
|
ctx.ext.append_debug_buffer("This is a test");
|
||||||
|
ctx.ext.append_debug_buffer("More text");
|
||||||
|
exec_trapped()
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut debug_buffer = Vec::new();
|
||||||
|
|
||||||
|
ExtBuilder::default().build().execute_with(|| {
|
||||||
|
let subsistence = Contracts::<Test>::subsistence_threshold();
|
||||||
|
let schedule = <Test as Config>::Schedule::get();
|
||||||
|
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
|
||||||
|
set_balance(&ALICE, subsistence * 10);
|
||||||
|
place_contract(&BOB, code_hash);
|
||||||
|
let result = MockStack::run_call(
|
||||||
|
ALICE,
|
||||||
|
BOB,
|
||||||
|
&mut gas_meter,
|
||||||
|
&schedule,
|
||||||
|
0,
|
||||||
|
vec![],
|
||||||
|
Some(&mut debug_buffer),
|
||||||
|
);
|
||||||
|
assert!(result.is_err());
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(&String::from_utf8(debug_buffer).unwrap(), "This is a testMore text");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,9 +59,6 @@
|
|||||||
//!
|
//!
|
||||||
//! ### Dispatchable functions
|
//! ### 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,
|
//! * [`Pallet::instantiate_with_code`] - Deploys a new contract from the supplied wasm binary,
|
||||||
//! optionally transferring
|
//! optionally transferring
|
||||||
//! some balance. This instantiates a new smart contract account with the supplied code and
|
//! some balance. This instantiates a new smart contract account with the supplied code and
|
||||||
@@ -160,6 +157,21 @@ pub mod pallet {
|
|||||||
/// Handler for rent payments.
|
/// Handler for rent payments.
|
||||||
type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
||||||
|
|
||||||
|
/// 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>;
|
||||||
|
|
||||||
|
/// Cost schedule and limits.
|
||||||
|
#[pallet::constant]
|
||||||
|
type Schedule: Get<Schedule<Self>>;
|
||||||
|
|
||||||
/// Number of block delay an extrinsic claim surcharge has.
|
/// Number of block delay an extrinsic claim surcharge has.
|
||||||
///
|
///
|
||||||
/// When claim surcharge is called by an extrinsic the rent is checked
|
/// When claim surcharge is called by an extrinsic the rent is checked
|
||||||
@@ -217,21 +229,6 @@ pub mod pallet {
|
|||||||
/// In other words only the origin called "root contract" is allowed to execute then.
|
/// In other words only the origin called "root contract" is allowed to execute then.
|
||||||
type CallStack: smallvec::Array<Item=Frame<Self>>;
|
type CallStack: smallvec::Array<Item=Frame<Self>>;
|
||||||
|
|
||||||
/// 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.
|
/// The maximum number of tries that can be queued for deletion.
|
||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type DeletionQueueDepth: Get<u32>;
|
type DeletionQueueDepth: Get<u32>;
|
||||||
@@ -239,12 +236,6 @@ pub mod pallet {
|
|||||||
/// The maximum amount of weight that can be consumed per block for lazy trie removal.
|
/// The maximum amount of weight that can be consumed per block for lazy trie removal.
|
||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type DeletionWeightLimit: Get<Weight>;
|
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]
|
#[pallet::pallet]
|
||||||
@@ -277,26 +268,6 @@ pub mod pallet {
|
|||||||
T::AccountId: UncheckedFrom<T::Hash>,
|
T::AccountId: UncheckedFrom<T::Hash>,
|
||||||
T::AccountId: AsRef<[u8]>,
|
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.
|
/// Makes a call to an account, optionally transferring some balance.
|
||||||
///
|
///
|
||||||
/// * If the account is a smart-contract account, the associated code will be
|
/// * If the account is a smart-contract account, the associated code will be
|
||||||
@@ -304,7 +275,9 @@ pub mod pallet {
|
|||||||
/// * If the account is a regular account, 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`,
|
/// * 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.
|
/// 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))]
|
#[pallet::weight(T::WeightInfo::call(T::Schedule::get().limits.code_len / 1024)
|
||||||
|
.saturating_add(*gas_limit)
|
||||||
|
)]
|
||||||
pub fn call(
|
pub fn call(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
dest: <T::Lookup as StaticLookup>::Source,
|
dest: <T::Lookup as StaticLookup>::Source,
|
||||||
@@ -315,9 +288,9 @@ pub mod pallet {
|
|||||||
let origin = ensure_signed(origin)?;
|
let origin = ensure_signed(origin)?;
|
||||||
let dest = T::Lookup::lookup(dest)?;
|
let dest = T::Lookup::lookup(dest)?;
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
let (result, code_len) = match ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
let (result, code_len) = match ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||||
origin, dest, &mut gas_meter, &schedule, value, data
|
origin, dest, &mut gas_meter, &schedule, value, data, None,
|
||||||
) {
|
) {
|
||||||
Ok((output, len)) => (Ok(output), len),
|
Ok((output, len)) => (Ok(output), len),
|
||||||
Err((err, len)) => (Err(err), len),
|
Err((err, len)) => (Err(err), len),
|
||||||
@@ -363,14 +336,14 @@ pub mod pallet {
|
|||||||
) -> DispatchResultWithPostInfo {
|
) -> DispatchResultWithPostInfo {
|
||||||
let origin = ensure_signed(origin)?;
|
let origin = ensure_signed(origin)?;
|
||||||
let code_len = code.len() as u32;
|
let code_len = code.len() as u32;
|
||||||
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
ensure!(code_len <= T::Schedule::get().limits.code_len, Error::<T>::CodeTooLarge);
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
let executable = PrefabWasmModule::from_code(code, &schedule)?;
|
||||||
let code_len = executable.code_len();
|
let code_len = executable.code_len();
|
||||||
ensure!(code_len <= T::MaxCodeSize::get(), Error::<T>::CodeTooLarge);
|
ensure!(code_len <= T::Schedule::get().limits.code_len, Error::<T>::CodeTooLarge);
|
||||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt, None,
|
||||||
).map(|(_address, output)| output);
|
).map(|(_address, output)| output);
|
||||||
gas_meter.into_dispatch_result(
|
gas_meter.into_dispatch_result(
|
||||||
result,
|
result,
|
||||||
@@ -384,8 +357,10 @@ pub mod pallet {
|
|||||||
/// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary
|
/// code deployment step. Instead, the `code_hash` of an on-chain deployed wasm binary
|
||||||
/// must be supplied.
|
/// must be supplied.
|
||||||
#[pallet::weight(
|
#[pallet::weight(
|
||||||
T::WeightInfo::instantiate(T::MaxCodeSize::get() / 1024, salt.len() as u32 / 1024)
|
T::WeightInfo::instantiate(
|
||||||
.saturating_add(*gas_limit)
|
T::Schedule::get().limits.code_len / 1024, salt.len() as u32 / 1024
|
||||||
|
)
|
||||||
|
.saturating_add(*gas_limit)
|
||||||
)]
|
)]
|
||||||
pub fn instantiate(
|
pub fn instantiate(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
@@ -397,11 +372,11 @@ pub mod pallet {
|
|||||||
) -> DispatchResultWithPostInfo {
|
) -> DispatchResultWithPostInfo {
|
||||||
let origin = ensure_signed(origin)?;
|
let origin = ensure_signed(origin)?;
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
||||||
let code_len = executable.code_len();
|
let code_len = executable.code_len();
|
||||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt, None,
|
||||||
).map(|(_address, output)| output);
|
).map(|(_address, output)| output);
|
||||||
gas_meter.into_dispatch_result(
|
gas_meter.into_dispatch_result(
|
||||||
result,
|
result,
|
||||||
@@ -418,7 +393,7 @@ pub mod pallet {
|
|||||||
///
|
///
|
||||||
/// If contract is not evicted as a result of this call, [`Error::ContractNotEvictable`]
|
/// If contract is not evicted as a result of this call, [`Error::ContractNotEvictable`]
|
||||||
/// is returned and the sender is not eligible for the reward.
|
/// is returned and the sender is not eligible for the reward.
|
||||||
#[pallet::weight(T::WeightInfo::claim_surcharge(T::MaxCodeSize::get() / 1024))]
|
#[pallet::weight(T::WeightInfo::claim_surcharge(T::Schedule::get().limits.code_len / 1024))]
|
||||||
pub fn claim_surcharge(
|
pub fn claim_surcharge(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
dest: T::AccountId,
|
dest: T::AccountId,
|
||||||
@@ -614,12 +589,10 @@ pub mod pallet {
|
|||||||
///
|
///
|
||||||
/// This can be triggered by a call to `seal_terminate` or `seal_restore_to`.
|
/// This can be triggered by a call to `seal_terminate` or `seal_restore_to`.
|
||||||
TerminatedInConstructor,
|
TerminatedInConstructor,
|
||||||
|
/// The debug message specified to `seal_debug_message` does contain invalid UTF-8.
|
||||||
|
DebugMessageInvalidUTF8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;
|
pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, CodeHash<T>, Vec<u8>>;
|
||||||
@@ -644,29 +617,6 @@ pub mod pallet {
|
|||||||
/// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.
|
/// stored in said trie. Therefore this operation is performed lazily in `on_initialize`.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
pub(crate) type DeletionQueue<T: Config> = StorageValue<_, Vec<DeletedContract>, ValueQuery>;
|
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>
|
impl<T: Config> Pallet<T>
|
||||||
@@ -678,6 +628,12 @@ where
|
|||||||
/// This function is similar to [`Self::call`], but doesn't perform any address lookups
|
/// This function is similar to [`Self::call`], but doesn't perform any address lookups
|
||||||
/// and better suitable for calling directly from Rust.
|
/// and better suitable for calling directly from Rust.
|
||||||
///
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// `debug` should only ever be set to `true` when executing as an RPC because
|
||||||
|
/// it adds allocations and could be abused to drive the runtime into an OOM panic.
|
||||||
|
/// If set to `true` it returns additional human readable debugging information.
|
||||||
|
///
|
||||||
/// It returns the execution result and the amount of used weight.
|
/// It returns the execution result and the amount of used weight.
|
||||||
pub fn bare_call(
|
pub fn bare_call(
|
||||||
origin: T::AccountId,
|
origin: T::AccountId,
|
||||||
@@ -685,17 +641,22 @@ where
|
|||||||
value: BalanceOf<T>,
|
value: BalanceOf<T>,
|
||||||
gas_limit: Weight,
|
gas_limit: Weight,
|
||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
|
debug: bool,
|
||||||
) -> ContractExecResult {
|
) -> ContractExecResult {
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
|
let mut debug_message = if debug {
|
||||||
|
Some(Vec::new())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||||
origin, dest, &mut gas_meter, &schedule, value, input_data,
|
origin, dest, &mut gas_meter, &schedule, value, input_data, debug_message.as_mut(),
|
||||||
);
|
);
|
||||||
let gas_consumed = gas_meter.gas_spent();
|
|
||||||
ContractExecResult {
|
ContractExecResult {
|
||||||
result: result.map(|r| r.0).map_err(|r| r.0.error),
|
result: result.map(|r| r.0).map_err(|r| r.0.error),
|
||||||
gas_consumed,
|
gas_consumed: gas_meter.gas_spent(),
|
||||||
debug_message: Bytes(Vec::new()),
|
debug_message: debug_message.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,6 +670,12 @@ where
|
|||||||
/// If `compute_projection` is set to `true` the result also contains the rent projection.
|
/// 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
|
/// This is optional because some non trivial and stateful work is performed to compute
|
||||||
/// the projection. See [`Self::rent_projection`].
|
/// the projection. See [`Self::rent_projection`].
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// `debug` should only ever be set to `true` when executing as an RPC because
|
||||||
|
/// it adds allocations and could be abused to drive the runtime into an OOM panic.
|
||||||
|
/// If set to `true` it returns additional human readable debugging information.
|
||||||
pub fn bare_instantiate(
|
pub fn bare_instantiate(
|
||||||
origin: T::AccountId,
|
origin: T::AccountId,
|
||||||
endowment: BalanceOf<T>,
|
endowment: BalanceOf<T>,
|
||||||
@@ -717,9 +684,10 @@ where
|
|||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
salt: Vec<u8>,
|
salt: Vec<u8>,
|
||||||
compute_projection: bool,
|
compute_projection: bool,
|
||||||
|
debug: bool,
|
||||||
) -> ContractInstantiateResult<T::AccountId, T::BlockNumber> {
|
) -> ContractInstantiateResult<T::AccountId, T::BlockNumber> {
|
||||||
let mut gas_meter = GasMeter::new(gas_limit);
|
let mut gas_meter = GasMeter::new(gas_limit);
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
let executable = match code {
|
let executable = match code {
|
||||||
Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),
|
Code::Upload(Bytes(binary)) => PrefabWasmModule::from_code(binary, &schedule),
|
||||||
Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),
|
Code::Existing(hash) => PrefabWasmModule::from_storage(hash, &schedule, &mut gas_meter),
|
||||||
@@ -729,11 +697,17 @@ where
|
|||||||
Err(error) => return ContractInstantiateResult {
|
Err(error) => return ContractInstantiateResult {
|
||||||
result: Err(error.into()),
|
result: Err(error.into()),
|
||||||
gas_consumed: gas_meter.gas_spent(),
|
gas_consumed: gas_meter.gas_spent(),
|
||||||
debug_message: Bytes(Vec::new()),
|
debug_message: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut debug_message = if debug {
|
||||||
|
Some(Vec::new())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt,
|
origin, executable, &mut gas_meter, &schedule,
|
||||||
|
endowment, data, &salt, debug_message.as_mut(),
|
||||||
).and_then(|(account_id, result)| {
|
).and_then(|(account_id, result)| {
|
||||||
let rent_projection = if compute_projection {
|
let rent_projection = if compute_projection {
|
||||||
Some(Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
Some(Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
||||||
@@ -751,7 +725,7 @@ where
|
|||||||
ContractInstantiateResult {
|
ContractInstantiateResult {
|
||||||
result: result.map_err(|e| e.error),
|
result: result.map_err(|e| e.error),
|
||||||
gas_consumed: gas_meter.gas_spent(),
|
gas_consumed: gas_meter.gas_spent(),
|
||||||
debug_message: Bytes(Vec::new()),
|
debug_message: debug_message.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,7 +796,7 @@ where
|
|||||||
/// Store code for benchmarks which does not check nor instrument the code.
|
/// Store code for benchmarks which does not check nor instrument the code.
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {
|
fn store_code_raw(code: Vec<u8>) -> frame_support::dispatch::DispatchResult {
|
||||||
let schedule = <CurrentSchedule<T>>::get();
|
let schedule = T::Schedule::get();
|
||||||
PrefabWasmModule::store_code_unchecked(code, &schedule)?;
|
PrefabWasmModule::store_code_unchecked(code, &schedule)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,24 +15,23 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::{Config, Weight, CurrentSchedule, Pallet, Schedule};
|
use crate::{Config, Weight, Pallet};
|
||||||
use frame_support::traits::{GetPalletVersion, PalletVersion, Get};
|
use frame_support::{
|
||||||
|
storage::migration,
|
||||||
|
traits::{GetPalletVersion, PalletVersion, PalletInfoAccess, Get},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn migrate<T: Config>() -> Weight {
|
pub fn migrate<T: Config>() -> Weight {
|
||||||
let mut weight: Weight = 0;
|
let mut weight: Weight = 0;
|
||||||
|
|
||||||
match <Pallet<T>>::storage_version() {
|
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) => {
|
Some(version) if version == PalletVersion::new(3, 0, 0) => {
|
||||||
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
|
weight = weight.saturating_add(T::DbWeight::get().writes(1));
|
||||||
let _ = <CurrentSchedule<T>>::translate::<u32, _>(|version| {
|
migration::remove_storage_prefix(
|
||||||
version.map(|version| Schedule {
|
<Pallet<T>>::name().as_bytes(),
|
||||||
version: version.saturating_add(1),
|
b"CurrentSchedule",
|
||||||
// Default limits were not decreased. Therefore it is OK to overwrite
|
b"",
|
||||||
// the schedule with the new defaults.
|
);
|
||||||
.. Default::default()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,27 +42,12 @@ pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
|
|||||||
/// Definition of the cost schedule and other parameterizations for the wasm vm.
|
/// 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
|
/// 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
|
/// callable functions without bumping to a new major version. The supplied [`Config::Schedule`]
|
||||||
/// rely on public functions of this type.
|
/// should rely on public functions of this type.
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
|
#[cfg_attr(feature = "std", serde(bound(serialize = "", deserialize = "")))]
|
||||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
|
#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug)]
|
||||||
pub struct Schedule<T: Config> {
|
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.
|
/// Describes the upper limits on various metrics.
|
||||||
pub(crate) limits: Limits,
|
pub(crate) limits: Limits,
|
||||||
|
|
||||||
@@ -73,12 +58,31 @@ pub struct Schedule<T: Config> {
|
|||||||
pub(crate) host_fn_weights: HostFnWeights<T>,
|
pub(crate) host_fn_weights: HostFnWeights<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Config> Schedule<T> {
|
||||||
|
/// Set the version of the instruction weights.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// Should be incremented whenever any instruction weight is changed. The
|
||||||
|
/// reason is that changes to instruction weights require a re-instrumentation
|
||||||
|
/// in order to apply the changes to an already deployed code. The re-instrumentation
|
||||||
|
/// is triggered by comparing the version of the current schedule with the the code was
|
||||||
|
/// instrumented with. Changes usually happen when pallet_contracts is re-benchmarked.
|
||||||
|
///
|
||||||
|
/// Changes to other parts of the schedule should not increment the version in
|
||||||
|
/// order to avoid unnecessary re-instrumentations.
|
||||||
|
pub fn set_instruction_weights_version(&mut self, version: u32) {
|
||||||
|
self.instruction_weights.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes the upper limits on various metrics.
|
/// Describes the upper limits on various metrics.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The values in this struct should only ever be increased for a deployed chain. The reason
|
/// The values in this struct should never be decreased. The reason is that decreasing those
|
||||||
/// is that decreasing those values will break existing contracts which are above the new limits.
|
/// values will break existing contracts which are above the new limits when a
|
||||||
|
/// re-instrumentation is triggered.
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
|
#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)]
|
||||||
pub struct Limits {
|
pub struct Limits {
|
||||||
@@ -121,6 +125,17 @@ pub struct Limits {
|
|||||||
|
|
||||||
/// The maximum length of a subject in bytes used for PRNG generation.
|
/// The maximum length of a subject in bytes used for PRNG generation.
|
||||||
pub subject_len: u32,
|
pub subject_len: u32,
|
||||||
|
|
||||||
|
/// The maximum nesting level of the call stack.
|
||||||
|
pub call_depth: u32,
|
||||||
|
|
||||||
|
/// The maximum size of a storage value and event payload in bytes.
|
||||||
|
pub payload_len: u32,
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub code_len: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Limits {
|
impl Limits {
|
||||||
@@ -153,6 +168,10 @@ impl Limits {
|
|||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
|
#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug)]
|
||||||
pub struct InstructionWeights<T: Config> {
|
pub struct InstructionWeights<T: Config> {
|
||||||
|
/// Version of the instruction weights.
|
||||||
|
///
|
||||||
|
/// See [`Schedule::set_instruction_weights_version`].
|
||||||
|
pub(crate) version: u32,
|
||||||
pub i64const: u32,
|
pub i64const: u32,
|
||||||
pub i64load: u32,
|
pub i64load: u32,
|
||||||
pub i64store: u32,
|
pub i64store: u32,
|
||||||
@@ -291,6 +310,9 @@ pub struct HostFnWeights<T: Config> {
|
|||||||
/// Weight per byte of an event deposited through `seal_deposit_event`.
|
/// Weight per byte of an event deposited through `seal_deposit_event`.
|
||||||
pub deposit_event_per_byte: Weight,
|
pub deposit_event_per_byte: Weight,
|
||||||
|
|
||||||
|
/// Weight of calling `seal_debug_message`.
|
||||||
|
pub debug_message: Weight,
|
||||||
|
|
||||||
/// Weight of calling `seal_set_rent_allowance`.
|
/// Weight of calling `seal_set_rent_allowance`.
|
||||||
pub set_rent_allowance: Weight,
|
pub set_rent_allowance: Weight,
|
||||||
|
|
||||||
@@ -454,8 +476,6 @@ macro_rules! cost_byte_batched {
|
|||||||
impl<T: Config> Default for Schedule<T> {
|
impl<T: Config> Default for Schedule<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
version: 0,
|
|
||||||
enable_println: false,
|
|
||||||
limits: Default::default(),
|
limits: Default::default(),
|
||||||
instruction_weights: Default::default(),
|
instruction_weights: Default::default(),
|
||||||
host_fn_weights: Default::default(),
|
host_fn_weights: Default::default(),
|
||||||
@@ -476,6 +496,9 @@ impl Default for Limits {
|
|||||||
table_size: 4096,
|
table_size: 4096,
|
||||||
br_table_size: 256,
|
br_table_size: 256,
|
||||||
subject_len: 32,
|
subject_len: 32,
|
||||||
|
call_depth: 32,
|
||||||
|
payload_len: 16 * 1024,
|
||||||
|
code_len: 128 * 1024,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,6 +507,7 @@ impl<T: Config> Default for InstructionWeights<T> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let max_pages = Limits::default().memory_pages;
|
let max_pages = Limits::default().memory_pages;
|
||||||
Self {
|
Self {
|
||||||
|
version: 2,
|
||||||
i64const: cost_instr!(instr_i64const, 1),
|
i64const: cost_instr!(instr_i64const, 1),
|
||||||
i64load: cost_instr!(instr_i64load, 2),
|
i64load: cost_instr!(instr_i64load, 2),
|
||||||
i64store: cost_instr!(instr_i64store, 2),
|
i64store: cost_instr!(instr_i64store, 2),
|
||||||
@@ -569,6 +593,7 @@ impl<T: Config> Default for HostFnWeights<T> {
|
|||||||
deposit_event: cost_batched!(seal_deposit_event),
|
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_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),
|
deposit_event_per_byte: cost_byte_batched_args!(seal_deposit_event_per_topic_and_kb, 0, 1),
|
||||||
|
debug_message: cost_batched!(seal_debug_message),
|
||||||
set_rent_allowance: cost_batched!(seal_set_rent_allowance),
|
set_rent_allowance: cost_batched!(seal_set_rent_allowance),
|
||||||
set_storage: cost_batched!(seal_set_storage),
|
set_storage: cost_batched!(seal_set_storage),
|
||||||
set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),
|
set_storage_per_byte: cost_byte_batched!(seal_set_storage_per_kb),
|
||||||
@@ -606,20 +631,6 @@ struct ScheduleRules<'a, T: Config> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Schedule<T> {
|
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 + '_ {
|
pub(crate) fn rules(&self, module: &elements::Module) -> impl rules::Rules + '_ {
|
||||||
ScheduleRules {
|
ScheduleRules {
|
||||||
schedule: &self,
|
schedule: &self,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use sp_io::hashing::blake2_256;
|
|||||||
use frame_support::{
|
use frame_support::{
|
||||||
assert_ok, assert_err, assert_err_ignore_postinfo,
|
assert_ok, assert_err, assert_err_ignore_postinfo,
|
||||||
parameter_types, assert_storage_noop,
|
parameter_types, assert_storage_noop,
|
||||||
traits::{Currency, ReservableCurrency, OnInitialize, GenesisBuild},
|
traits::{Currency, ReservableCurrency, OnInitialize},
|
||||||
weights::{Weight, PostDispatchInfo, DispatchClass, constants::WEIGHT_PER_SECOND},
|
weights::{Weight, PostDispatchInfo, DispatchClass, constants::WEIGHT_PER_SECOND},
|
||||||
dispatch::DispatchErrorWithPostInfo,
|
dispatch::DispatchErrorWithPostInfo,
|
||||||
storage::child,
|
storage::child,
|
||||||
@@ -63,7 +63,7 @@ frame_support::construct_runtime!(
|
|||||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
||||||
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
|
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
|
||||||
Randomness: pallet_randomness_collective_flip::{Pallet, Call, Storage},
|
Randomness: pallet_randomness_collective_flip::{Pallet, Call, Storage},
|
||||||
Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},
|
Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -265,6 +265,7 @@ parameter_types! {
|
|||||||
pub const DeletionQueueDepth: u32 = 1024;
|
pub const DeletionQueueDepth: u32 = 1024;
|
||||||
pub const DeletionWeightLimit: Weight = 500_000_000_000;
|
pub const DeletionWeightLimit: Weight = 500_000_000_000;
|
||||||
pub const MaxCodeSize: u32 = 2 * 1024;
|
pub const MaxCodeSize: u32 = 2 * 1024;
|
||||||
|
pub MySchedule: Schedule<Test> = <Schedule<Test>>::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
@@ -291,13 +292,12 @@ impl Config for Test {
|
|||||||
type RentFraction = RentFraction;
|
type RentFraction = RentFraction;
|
||||||
type SurchargeReward = SurchargeReward;
|
type SurchargeReward = SurchargeReward;
|
||||||
type CallStack = [Frame<Self>; 31];
|
type CallStack = [Frame<Self>; 31];
|
||||||
type MaxValueSize = MaxValueSize;
|
|
||||||
type WeightPrice = Self;
|
type WeightPrice = Self;
|
||||||
type WeightInfo = ();
|
type WeightInfo = ();
|
||||||
type ChainExtension = TestExtension;
|
type ChainExtension = TestExtension;
|
||||||
type DeletionQueueDepth = DeletionQueueDepth;
|
type DeletionQueueDepth = DeletionQueueDepth;
|
||||||
type DeletionWeightLimit = DeletionWeightLimit;
|
type DeletionWeightLimit = DeletionWeightLimit;
|
||||||
type MaxCodeSize = MaxCodeSize;
|
type Schedule = MySchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
|
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
|
||||||
@@ -331,12 +331,6 @@ impl ExtBuilder {
|
|||||||
pallet_balances::GenesisConfig::<Test> {
|
pallet_balances::GenesisConfig::<Test> {
|
||||||
balances: vec![],
|
balances: vec![],
|
||||||
}.assimilate_storage(&mut t).unwrap();
|
}.assimilate_storage(&mut t).unwrap();
|
||||||
pallet_contracts::GenesisConfig {
|
|
||||||
current_schedule: Schedule::<Test> {
|
|
||||||
enable_println: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
}.assimilate_storage(&mut t).unwrap();
|
|
||||||
let mut ext = sp_io::TestExternalities::new(t);
|
let mut ext = sp_io::TestExternalities::new(t);
|
||||||
ext.execute_with(|| System::set_block_number(1));
|
ext.execute_with(|| System::set_block_number(1));
|
||||||
ext
|
ext
|
||||||
@@ -564,7 +558,7 @@ fn deposit_event_max_value_limit() {
|
|||||||
addr.clone(),
|
addr.clone(),
|
||||||
0,
|
0,
|
||||||
GAS_LIMIT * 2, // we are copying a huge buffer,
|
GAS_LIMIT * 2, // we are copying a huge buffer,
|
||||||
<Test as Config>::MaxValueSize::get().encode(),
|
<Test as Config>::Schedule::get().limits.payload_len.encode(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Call contract with too large a storage value.
|
// Call contract with too large a storage value.
|
||||||
@@ -574,7 +568,7 @@ fn deposit_event_max_value_limit() {
|
|||||||
addr,
|
addr,
|
||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
(<Test as Config>::MaxValueSize::get() + 1).encode(),
|
(<Test as Config>::Schedule::get().limits.payload_len + 1).encode(),
|
||||||
),
|
),
|
||||||
Error::<Test>::ValueTooLarge,
|
Error::<Test>::ValueTooLarge,
|
||||||
);
|
);
|
||||||
@@ -1544,7 +1538,7 @@ fn storage_max_value_limit() {
|
|||||||
addr.clone(),
|
addr.clone(),
|
||||||
0,
|
0,
|
||||||
GAS_LIMIT * 2, // we are copying a huge buffer
|
GAS_LIMIT * 2, // we are copying a huge buffer
|
||||||
<Test as Config>::MaxValueSize::get().encode(),
|
<Test as Config>::Schedule::get().limits.payload_len.encode(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Call contract with too large a storage value.
|
// Call contract with too large a storage value.
|
||||||
@@ -1554,7 +1548,7 @@ fn storage_max_value_limit() {
|
|||||||
addr,
|
addr,
|
||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
(<Test as Config>::MaxValueSize::get() + 1).encode(),
|
(<Test as Config>::Schedule::get().limits.payload_len + 1).encode(),
|
||||||
),
|
),
|
||||||
Error::<Test>::ValueTooLarge,
|
Error::<Test>::ValueTooLarge,
|
||||||
);
|
);
|
||||||
@@ -1896,6 +1890,7 @@ fn crypto_hashes() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
params,
|
params,
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert!(result.is_success());
|
assert!(result.is_success());
|
||||||
let expected = hash_fn(input.as_ref());
|
let expected = hash_fn(input.as_ref());
|
||||||
@@ -1931,6 +1926,7 @@ fn transfer_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![],
|
vec![],
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
||||||
|
|
||||||
@@ -1945,6 +1941,7 @@ fn transfer_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![],
|
vec![],
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
||||||
});
|
});
|
||||||
@@ -1979,6 +1976,7 @@ fn call_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),
|
AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::NotCallable);
|
assert_return_code!(result, RuntimeReturnCode::NotCallable);
|
||||||
|
|
||||||
@@ -2002,6 +2000,7 @@ fn call_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&0u32.to_le_bytes()).cloned().collect(),
|
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&0u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
||||||
|
|
||||||
@@ -2016,6 +2015,7 @@ fn call_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&0u32.to_le_bytes()).cloned().collect(),
|
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&0u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
||||||
|
|
||||||
@@ -2027,6 +2027,7 @@ fn call_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&1u32.to_le_bytes()).cloned().collect(),
|
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&1u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
|
assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
|
||||||
|
|
||||||
@@ -2037,6 +2038,7 @@ fn call_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&2u32.to_le_bytes()).cloned().collect(),
|
AsRef::<[u8]>::as_ref(&addr_django).iter().chain(&2u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
|
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
|
||||||
|
|
||||||
@@ -2084,6 +2086,7 @@ fn instantiate_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
callee_hash.clone(),
|
callee_hash.clone(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
assert_return_code!(result, RuntimeReturnCode::BelowSubsistenceThreshold);
|
||||||
|
|
||||||
@@ -2098,6 +2101,7 @@ fn instantiate_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
callee_hash.clone(),
|
callee_hash.clone(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
|
||||||
|
|
||||||
@@ -2109,6 +2113,7 @@ fn instantiate_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![0; 33],
|
vec![0; 33],
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
|
assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
|
||||||
|
|
||||||
@@ -2119,6 +2124,7 @@ fn instantiate_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(),
|
callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
|
assert_return_code!(result, RuntimeReturnCode::CalleeReverted);
|
||||||
|
|
||||||
@@ -2129,6 +2135,7 @@ fn instantiate_return_code() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(),
|
callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(),
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
|
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
|
||||||
|
|
||||||
@@ -2216,6 +2223,7 @@ fn chain_extension_works() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![0, 99],
|
vec![0, 99],
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
let gas_consumed = result.gas_consumed;
|
let gas_consumed = result.gas_consumed;
|
||||||
assert_eq!(TestExtension::last_seen_buffer(), vec![0, 99]);
|
assert_eq!(TestExtension::last_seen_buffer(), vec![0, 99]);
|
||||||
@@ -2228,6 +2236,7 @@ fn chain_extension_works() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![1],
|
vec![1],
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
// those values passed in the fixture
|
// those values passed in the fixture
|
||||||
assert_eq!(TestExtension::last_seen_inputs(), (4, 1, 16, 12));
|
assert_eq!(TestExtension::last_seen_inputs(), (4, 1, 16, 12));
|
||||||
@@ -2239,6 +2248,7 @@ fn chain_extension_works() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![2, 42],
|
vec![2, 42],
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
assert_ok!(result.result);
|
assert_ok!(result.result);
|
||||||
assert_eq!(result.gas_consumed, gas_consumed + 42);
|
assert_eq!(result.gas_consumed, gas_consumed + 42);
|
||||||
@@ -2250,6 +2260,7 @@ fn chain_extension_works() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
vec![3],
|
vec![3],
|
||||||
|
false,
|
||||||
).result.unwrap();
|
).result.unwrap();
|
||||||
assert_eq!(result.flags, ReturnFlags::REVERT);
|
assert_eq!(result.flags, ReturnFlags::REVERT);
|
||||||
assert_eq!(result.data, Bytes(vec![42, 99]));
|
assert_eq!(result.data, Bytes(vec![42, 99]));
|
||||||
@@ -2782,6 +2793,7 @@ fn reinstrument_does_charge() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
zero.clone(),
|
zero.clone(),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
assert!(result0.result.unwrap().is_success());
|
assert!(result0.result.unwrap().is_success());
|
||||||
|
|
||||||
@@ -2791,15 +2803,17 @@ fn reinstrument_does_charge() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
zero.clone(),
|
zero.clone(),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
assert!(result1.result.unwrap().is_success());
|
assert!(result1.result.unwrap().is_success());
|
||||||
|
|
||||||
// They should match because both where called with the same schedule.
|
// They should match because both where called with the same schedule.
|
||||||
assert_eq!(result0.gas_consumed, result1.gas_consumed);
|
assert_eq!(result0.gas_consumed, result1.gas_consumed);
|
||||||
|
|
||||||
// Update the schedule version but keep the rest the same
|
// We cannot change the schedule. Instead, we decrease the version of the deployed
|
||||||
crate::CurrentSchedule::mutate(|old: &mut Schedule<Test>| {
|
// contract below the current schedule's version.
|
||||||
old.version += 1;
|
crate::CodeStorage::mutate(&code_hash, |code: &mut Option<PrefabWasmModule<Test>>| {
|
||||||
|
code.as_mut().unwrap().decrement_version();
|
||||||
});
|
});
|
||||||
|
|
||||||
// This call should trigger reinstrumentation
|
// This call should trigger reinstrumentation
|
||||||
@@ -2809,6 +2823,7 @@ fn reinstrument_does_charge() {
|
|||||||
0,
|
0,
|
||||||
GAS_LIMIT,
|
GAS_LIMIT,
|
||||||
zero.clone(),
|
zero.clone(),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
assert!(result2.result.unwrap().is_success());
|
assert!(result2.result.unwrap().is_success());
|
||||||
assert!(result2.gas_consumed > result1.gas_consumed);
|
assert!(result2.gas_consumed > result1.gas_consumed);
|
||||||
@@ -2818,3 +2833,102 @@ fn reinstrument_does_charge() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_message_works() {
|
||||||
|
let (wasm, code_hash) = compile_module::<Test>("debug_message_works").unwrap();
|
||||||
|
|
||||||
|
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||||
|
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||||
|
assert_ok!(
|
||||||
|
Contracts::instantiate_with_code(
|
||||||
|
Origin::signed(ALICE),
|
||||||
|
30_000,
|
||||||
|
GAS_LIMIT,
|
||||||
|
wasm,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
|
||||||
|
let result = Contracts::bare_call(
|
||||||
|
ALICE,
|
||||||
|
addr,
|
||||||
|
0,
|
||||||
|
GAS_LIMIT,
|
||||||
|
vec![],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_matches!(result.result, Ok(_));
|
||||||
|
assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_message_logging_disabled() {
|
||||||
|
let (wasm, code_hash) = compile_module::<Test>("debug_message_logging_disabled").unwrap();
|
||||||
|
|
||||||
|
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||||
|
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||||
|
assert_ok!(
|
||||||
|
Contracts::instantiate_with_code(
|
||||||
|
Origin::signed(ALICE),
|
||||||
|
30_000,
|
||||||
|
GAS_LIMIT,
|
||||||
|
wasm,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
|
||||||
|
// disable logging by passing `false`
|
||||||
|
let result = Contracts::bare_call(
|
||||||
|
ALICE,
|
||||||
|
addr.clone(),
|
||||||
|
0,
|
||||||
|
GAS_LIMIT,
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert_matches!(result.result, Ok(_));
|
||||||
|
// the dispatchables always run without debugging
|
||||||
|
assert_ok!(Contracts::call(
|
||||||
|
Origin::signed(ALICE),
|
||||||
|
addr,
|
||||||
|
0,
|
||||||
|
GAS_LIMIT,
|
||||||
|
vec![],
|
||||||
|
));
|
||||||
|
assert!(result.debug_message.is_empty());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_message_invalid_utf8() {
|
||||||
|
let (wasm, code_hash) = compile_module::<Test>("debug_message_invalid_utf8").unwrap();
|
||||||
|
|
||||||
|
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||||
|
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||||
|
assert_ok!(
|
||||||
|
Contracts::instantiate_with_code(
|
||||||
|
Origin::signed(ALICE),
|
||||||
|
30_000,
|
||||||
|
GAS_LIMIT,
|
||||||
|
wasm,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
|
||||||
|
let result = Contracts::bare_call(
|
||||||
|
ALICE,
|
||||||
|
addr,
|
||||||
|
0,
|
||||||
|
GAS_LIMIT,
|
||||||
|
vec![],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert_err!(result.result, <Error<Test>>::DebugMessageInvalidUTF8);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,11 +132,9 @@ where
|
|||||||
prefab_module.code_hash = code_hash;
|
prefab_module.code_hash = code_hash;
|
||||||
|
|
||||||
if let Some((schedule, gas_meter)) = reinstrument {
|
if let Some((schedule, gas_meter)) = reinstrument {
|
||||||
if prefab_module.schedule_version < schedule.version {
|
if prefab_module.instruction_weights_version < schedule.instruction_weights.version {
|
||||||
// The current schedule version is greater than the version of the one cached
|
// The instruction weights have changed.
|
||||||
// in the storage.
|
// We need to re-instrument the code with the new instruction weights.
|
||||||
//
|
|
||||||
// We need to re-instrument the code with the latest schedule here.
|
|
||||||
gas_meter.charge(InstrumentToken(prefab_module.original_code_len))?;
|
gas_meter.charge(InstrumentToken(prefab_module.original_code_len))?;
|
||||||
private::reinstrument(&mut prefab_module, schedule)?;
|
private::reinstrument(&mut prefab_module, schedule)?;
|
||||||
}
|
}
|
||||||
@@ -158,7 +156,7 @@ mod private {
|
|||||||
let original_code = <PristineCode<T>>::get(&prefab_module.code_hash)
|
let original_code = <PristineCode<T>>::get(&prefab_module.code_hash)
|
||||||
.ok_or_else(|| Error::<T>::CodeNotFound)?;
|
.ok_or_else(|| Error::<T>::CodeNotFound)?;
|
||||||
prefab_module.code = prepare::reinstrument_contract::<T>(original_code, schedule)?;
|
prefab_module.code = prepare::reinstrument_contract::<T>(original_code, schedule)?;
|
||||||
prefab_module.schedule_version = schedule.version;
|
prefab_module.instruction_weights_version = schedule.instruction_weights.version;
|
||||||
<CodeStorage<T>>::insert(&prefab_module.code_hash, &*prefab_module);
|
<CodeStorage<T>>::insert(&prefab_module.code_hash, &*prefab_module);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,16 +45,16 @@ pub use tests::MockExt;
|
|||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// This data structure is mostly immutable once created and stored. The exceptions that
|
/// This data structure is mostly immutable once created and stored. The exceptions that
|
||||||
/// can be changed by calling a contract are `refcount`, `schedule_version` and `code`.
|
/// can be changed by calling a contract are `refcount`, `instruction_weights_version` and `code`.
|
||||||
/// `refcount` can change when a contract instantiates a new contract or self terminates.
|
/// `refcount` can change when a contract instantiates a new contract or self terminates.
|
||||||
/// `schedule_version` and `code` when a contract with an outdated instrumention is called.
|
/// `instruction_weights_version` and `code` when a contract with an outdated instrumention is
|
||||||
/// Therefore one must be careful when holding any in-memory representation of this type while
|
/// called. Therefore one must be careful when holding any in-memory representation of this
|
||||||
/// calling into a contract as those fields can get out of date.
|
/// type while calling into a contract as those fields can get out of date.
|
||||||
#[derive(Clone, Encode, Decode)]
|
#[derive(Clone, Encode, Decode)]
|
||||||
pub struct PrefabWasmModule<T: Config> {
|
pub struct PrefabWasmModule<T: Config> {
|
||||||
/// Version of the schedule with which the code was instrumented.
|
/// Version of the instruction weights with which the code was instrumented.
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
schedule_version: u32,
|
instruction_weights_version: u32,
|
||||||
/// Initial memory size of a contract's sandbox.
|
/// Initial memory size of a contract's sandbox.
|
||||||
#[codec(compact)]
|
#[codec(compact)]
|
||||||
initial: u32,
|
initial: u32,
|
||||||
@@ -140,6 +140,12 @@ where
|
|||||||
pub fn refcount(&self) -> u64 {
|
pub fn refcount(&self) -> u64 {
|
||||||
self.refcount
|
self.refcount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decrement instruction_weights_version by 1. Panics if it is already 0.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn decrement_version(&mut self) {
|
||||||
|
self.instruction_weights_version = self.instruction_weights_version.checked_sub(1).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Executable<T> for PrefabWasmModule<T>
|
impl<T: Config> Executable<T> for PrefabWasmModule<T>
|
||||||
@@ -297,6 +303,7 @@ mod tests {
|
|||||||
schedule: Schedule<Test>,
|
schedule: Schedule<Test>,
|
||||||
rent_params: RentParams<Test>,
|
rent_params: RentParams<Test>,
|
||||||
gas_meter: GasMeter<Test>,
|
gas_meter: GasMeter<Test>,
|
||||||
|
debug_buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MockExt {
|
impl Default for MockExt {
|
||||||
@@ -312,6 +319,7 @@ mod tests {
|
|||||||
schedule: Default::default(),
|
schedule: Default::default(),
|
||||||
rent_params: Default::default(),
|
rent_params: Default::default(),
|
||||||
gas_meter: GasMeter::new(10_000_000_000),
|
gas_meter: GasMeter::new(10_000_000_000),
|
||||||
|
debug_buffer: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,6 +455,10 @@ mod tests {
|
|||||||
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
fn gas_meter(&mut self) -> &mut GasMeter<Self::T> {
|
||||||
&mut self.gas_meter
|
&mut self.gas_meter
|
||||||
}
|
}
|
||||||
|
fn append_debug_buffer(&mut self, msg: &str) -> bool {
|
||||||
|
self.debug_buffer.extend(msg.as_bytes());
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute<E: BorrowMut<MockExt>>(
|
fn execute<E: BorrowMut<MockExt>>(
|
||||||
@@ -1845,4 +1857,71 @@ mod tests {
|
|||||||
let rent_params = Bytes(<RentParams<Test>>::default().encode());
|
let rent_params = Bytes(<RentParams<Test>>::default().encode());
|
||||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_params });
|
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CODE_DEBUG_MESSAGE: &str = r#"
|
||||||
|
(module
|
||||||
|
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(data (i32.const 0) "Hello World!")
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
(call $seal_debug_message
|
||||||
|
(i32.const 0) ;; Pointer to the text buffer
|
||||||
|
(i32.const 12) ;; The size of the buffer
|
||||||
|
)
|
||||||
|
drop
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "deploy"))
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_message_works() {
|
||||||
|
let mut ext = MockExt::default();
|
||||||
|
execute(
|
||||||
|
CODE_DEBUG_MESSAGE,
|
||||||
|
vec![],
|
||||||
|
&mut ext,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(std::str::from_utf8(&ext.debug_buffer).unwrap(), "Hello World!");
|
||||||
|
}
|
||||||
|
|
||||||
|
const CODE_DEBUG_MESSAGE_FAIL: &str = r#"
|
||||||
|
(module
|
||||||
|
(import "seal0" "seal_debug_message" (func $seal_debug_message (param i32 i32) (result i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(data (i32.const 0) "\fc")
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
(call $seal_debug_message
|
||||||
|
(i32.const 0) ;; Pointer to the text buffer
|
||||||
|
(i32.const 1) ;; The size of the buffer
|
||||||
|
)
|
||||||
|
drop
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "deploy"))
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_message_invalid_utf8_fails() {
|
||||||
|
let mut ext = MockExt::default();
|
||||||
|
let result = execute(
|
||||||
|
CODE_DEBUG_MESSAGE_FAIL,
|
||||||
|
vec![],
|
||||||
|
&mut ext,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Err(ExecError {
|
||||||
|
error: Error::<Test>::DebugMessageInvalidUTF8.into(),
|
||||||
|
origin: ErrorOrigin::Caller,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,12 +343,6 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
|||||||
.get(*type_idx as usize)
|
.get(*type_idx as usize)
|
||||||
.ok_or_else(|| "validation: import entry points to a non-existent type")?;
|
.ok_or_else(|| "validation: import entry points to a non-existent type")?;
|
||||||
|
|
||||||
// We disallow importing `seal_println` unless debug features are enabled,
|
|
||||||
// which should only be allowed on a dev chain
|
|
||||||
if !self.schedule.enable_println && import.field().as_bytes() == b"seal_println" {
|
|
||||||
return Err("module imports `seal_println` but debug features disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
if !T::ChainExtension::enabled() &&
|
if !T::ChainExtension::enabled() &&
|
||||||
import.field().as_bytes() == b"seal_call_chain_extension"
|
import.field().as_bytes() == b"seal_call_chain_extension"
|
||||||
{
|
{
|
||||||
@@ -439,7 +433,7 @@ fn do_preparation<C: ImportSatisfyCheck, T: Config>(
|
|||||||
schedule,
|
schedule,
|
||||||
)?;
|
)?;
|
||||||
Ok(PrefabWasmModule {
|
Ok(PrefabWasmModule {
|
||||||
schedule_version: schedule.version,
|
instruction_weights_version: schedule.instruction_weights.version,
|
||||||
initial,
|
initial,
|
||||||
maximum,
|
maximum,
|
||||||
_reserved: None,
|
_reserved: None,
|
||||||
@@ -505,7 +499,7 @@ pub mod benchmarking {
|
|||||||
let contract_module = ContractModule::new(&original_code, schedule)?;
|
let contract_module = ContractModule::new(&original_code, schedule)?;
|
||||||
let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?;
|
let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?;
|
||||||
Ok(PrefabWasmModule {
|
Ok(PrefabWasmModule {
|
||||||
schedule_version: schedule.version,
|
instruction_weights_version: schedule.instruction_weights.version,
|
||||||
initial: memory_limits.0,
|
initial: memory_limits.0,
|
||||||
maximum: memory_limits.1,
|
maximum: memory_limits.1,
|
||||||
_reserved: None,
|
_reserved: None,
|
||||||
@@ -547,8 +541,6 @@ mod tests {
|
|||||||
|
|
||||||
// new version of nop with other data type for argumebt
|
// new version of nop with other data type for argumebt
|
||||||
[seal1] nop(_ctx, _unused: i32) => { unreachable!(); },
|
[seal1] nop(_ctx, _unused: i32) => { unreachable!(); },
|
||||||
|
|
||||||
[seal0] seal_println(_ctx, _ptr: u32, _len: u32) => { unreachable!(); },
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,36 +930,6 @@ mod tests {
|
|||||||
"#,
|
"#,
|
||||||
Err("module imports a non-existent function")
|
Err("module imports a non-existent function")
|
||||||
);
|
);
|
||||||
|
|
||||||
prepare_test!(seal_println_debug_disabled,
|
|
||||||
r#"
|
|
||||||
(module
|
|
||||||
(import "seal0" "seal_println" (func $seal_println (param i32 i32)))
|
|
||||||
|
|
||||||
(func (export "call"))
|
|
||||||
(func (export "deploy"))
|
|
||||||
)
|
|
||||||
"#,
|
|
||||||
Err("module imports `seal_println` but debug features disabled")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seal_println_debug_enabled() {
|
|
||||||
let wasm = wat::parse_str(
|
|
||||||
r#"
|
|
||||||
(module
|
|
||||||
(import "seal0" "seal_println" (func $seal_println (param i32 i32)))
|
|
||||||
|
|
||||||
(func (export "call"))
|
|
||||||
(func (export "deploy"))
|
|
||||||
)
|
|
||||||
"#
|
|
||||||
).unwrap();
|
|
||||||
let mut schedule = Schedule::default();
|
|
||||||
schedule.enable_println = true;
|
|
||||||
let r = do_preparation::<env::Test, crate::tests::Test>(wasm, &schedule);
|
|
||||||
assert_matches::assert_matches!(r, Ok(_));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod entrypoints {
|
mod entrypoints {
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ pub enum ReturnCode {
|
|||||||
/// The contract that was called is either no contract at all (a plain account)
|
/// The contract that was called is either no contract at all (a plain account)
|
||||||
/// or is a tombstone.
|
/// or is a tombstone.
|
||||||
NotCallable = 8,
|
NotCallable = 8,
|
||||||
|
/// The call to `seal_debug_message` had no effect because debug message
|
||||||
|
/// recording was disabled.
|
||||||
|
LoggingDisabled = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConvertibleToWasm for ReturnCode {
|
impl ConvertibleToWasm for ReturnCode {
|
||||||
@@ -175,6 +178,8 @@ pub enum RuntimeCosts {
|
|||||||
Random,
|
Random,
|
||||||
/// Weight of calling `seal_deposit_event` with the given number of topics and event size.
|
/// Weight of calling `seal_deposit_event` with the given number of topics and event size.
|
||||||
DepositEvent{num_topic: u32, len: u32},
|
DepositEvent{num_topic: u32, len: u32},
|
||||||
|
/// Weight of calling `seal_debug_message`.
|
||||||
|
DebugMessage,
|
||||||
/// Weight of calling `seal_set_rent_allowance`.
|
/// Weight of calling `seal_set_rent_allowance`.
|
||||||
SetRentAllowance,
|
SetRentAllowance,
|
||||||
/// Weight of calling `seal_set_storage` for the given storage item size.
|
/// Weight of calling `seal_set_storage` for the given storage item size.
|
||||||
@@ -255,6 +260,7 @@ impl RuntimeCosts {
|
|||||||
DepositEvent{num_topic, len} => s.deposit_event
|
DepositEvent{num_topic, len} => s.deposit_event
|
||||||
.saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into()))
|
.saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into()))
|
||||||
.saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())),
|
.saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())),
|
||||||
|
DebugMessage => s.debug_message,
|
||||||
SetRentAllowance => s.set_rent_allowance,
|
SetRentAllowance => s.set_rent_allowance,
|
||||||
SetStorage(len) => s.set_storage
|
SetStorage(len) => s.set_storage
|
||||||
.saturating_add(s.set_storage_per_byte.saturating_mul(len.into())),
|
.saturating_add(s.set_storage_per_byte.saturating_mul(len.into())),
|
||||||
@@ -802,7 +808,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
ctx.charge_gas(RuntimeCosts::CallSurchargeTransfer)?;
|
ctx.charge_gas(RuntimeCosts::CallSurchargeTransfer)?;
|
||||||
}
|
}
|
||||||
let charged = ctx.charge_gas(
|
let charged = ctx.charge_gas(
|
||||||
RuntimeCosts::CallSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
RuntimeCosts::CallSurchargeCodeSize(<E::T as Config>::Schedule::get().limits.code_len)
|
||||||
)?;
|
)?;
|
||||||
let ext = &mut ctx.ext;
|
let ext = &mut ctx.ext;
|
||||||
let call_outcome = ext.call(gas, callee, value, input_data);
|
let call_outcome = ext.call(gas, callee, value, input_data);
|
||||||
@@ -887,7 +893,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||||
let salt = ctx.read_sandbox_memory(salt_ptr, salt_len)?;
|
let salt = ctx.read_sandbox_memory(salt_ptr, salt_len)?;
|
||||||
let charged = ctx.charge_gas(
|
let charged = ctx.charge_gas(
|
||||||
RuntimeCosts::InstantiateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
RuntimeCosts::InstantiateSurchargeCodeSize(
|
||||||
|
<E::T as Config>::Schedule::get().limits.code_len
|
||||||
|
)
|
||||||
)?;
|
)?;
|
||||||
let ext = &mut ctx.ext;
|
let ext = &mut ctx.ext;
|
||||||
let instantiate_outcome = ext.instantiate(gas, code_hash, value, input_data, &salt);
|
let instantiate_outcome = ext.instantiate(gas, code_hash, value, input_data, &salt);
|
||||||
@@ -937,7 +945,9 @@ define_env!(Env, <E: Ext>,
|
|||||||
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
||||||
|
|
||||||
let charged = ctx.charge_gas(
|
let charged = ctx.charge_gas(
|
||||||
RuntimeCosts::TerminateSurchargeCodeSize(<E::T as Config>::MaxCodeSize::get())
|
RuntimeCosts::TerminateSurchargeCodeSize(
|
||||||
|
<E::T as Config>::Schedule::get().limits.code_len
|
||||||
|
)
|
||||||
)?;
|
)?;
|
||||||
let (result, code_len) = match ctx.ext.terminate(&beneficiary) {
|
let (result, code_len) = match ctx.ext.terminate(&beneficiary) {
|
||||||
Ok(len) => (Ok(()), len),
|
Ok(len) => (Ok(()), len),
|
||||||
@@ -1266,7 +1276,7 @@ define_env!(Env, <E: Ext>,
|
|||||||
delta
|
delta
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_len = <E::T as Config>::MaxCodeSize::get();
|
let max_len = <E::T as Config>::Schedule::get().limits.code_len;
|
||||||
let charged = ctx.charge_gas(RuntimeCosts::RestoreToSurchargeCodeSize {
|
let charged = ctx.charge_gas(RuntimeCosts::RestoreToSurchargeCodeSize {
|
||||||
caller_code: max_len,
|
caller_code: max_len,
|
||||||
tombstone_code: max_len,
|
tombstone_code: max_len,
|
||||||
@@ -1380,15 +1390,33 @@ define_env!(Env, <E: Ext>,
|
|||||||
)?)
|
)?)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Prints utf8 encoded string from the data buffer.
|
// Emit a custom debug message.
|
||||||
// Only available on `--dev` chains.
|
//
|
||||||
// This function may be removed at any time, superseded by a more general contract debugging feature.
|
// No newlines are added to the supplied message.
|
||||||
[seal0] seal_println(ctx, str_ptr: u32, str_len: u32) => {
|
// Specifying invalid UTF-8 triggers a trap.
|
||||||
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
|
//
|
||||||
if let Ok(utf8) = core::str::from_utf8(&data) {
|
// This is a no-op if debug message recording is disabled which is always the case
|
||||||
log::info!(target: "runtime::contracts", "seal_println: {}", utf8);
|
// when the code is executing on-chain. The message is interpreted as UTF-8 and
|
||||||
|
// appended to the debug buffer which is then supplied to the calling RPC client.
|
||||||
|
//
|
||||||
|
// # Note
|
||||||
|
//
|
||||||
|
// Even though no action is taken when debug message recording is disabled there is still
|
||||||
|
// a non trivial overhead (and weight cost) associated with calling this function. Contract
|
||||||
|
// languages should remove calls to this function (either at runtime or compile time) when
|
||||||
|
// not being executed as an RPC. For example, they could allow users to disable logging
|
||||||
|
// through compile time flags (cargo features) for on-chain deployment. Additionally, the
|
||||||
|
// return value of this function can be cached in order to prevent further calls at runtime.
|
||||||
|
[seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => {
|
||||||
|
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
|
||||||
|
if ctx.ext.append_debug_buffer("") {
|
||||||
|
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
|
||||||
|
let msg = core::str::from_utf8(&data)
|
||||||
|
.map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
|
||||||
|
ctx.ext.append_debug_buffer(msg);
|
||||||
|
return Ok(ReturnCode::Success);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(ReturnCode::LoggingDisabled)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Stores the current block number of the current contract into the supplied buffer.
|
// Stores the current block number of the current contract into the supplied buffer.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user