mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -25,21 +25,20 @@
|
||||
//! compiles it down into a `WasmModule` that can be used as a contract's code.
|
||||
|
||||
use crate::Config;
|
||||
use frame_support::traits::Get;
|
||||
use pwasm_utils::{
|
||||
stack_height::inject_limiter,
|
||||
parity_wasm::{
|
||||
elements::{
|
||||
self, Instruction, Instructions, FuncBody, ValueType, BlockType, Section,
|
||||
CustomSection,
|
||||
},
|
||||
builder,
|
||||
elements::{
|
||||
self, BlockType, CustomSection, FuncBody, Instruction, Instructions, Section, ValueType,
|
||||
},
|
||||
},
|
||||
stack_height::inject_limiter,
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
|
||||
use sp_std::{prelude::*, convert::TryFrom, borrow::ToOwned};
|
||||
use frame_support::traits::Get;
|
||||
use sp_std::{borrow::ToOwned, convert::TryFrom, prelude::*};
|
||||
|
||||
/// Pass to `create_code` in order to create a compiled `WasmModule`.
|
||||
///
|
||||
@@ -117,7 +116,7 @@ pub struct ImportedFunction {
|
||||
|
||||
/// A wasm module ready to be put on chain.
|
||||
#[derive(Clone)]
|
||||
pub struct WasmModule<T:Config> {
|
||||
pub struct WasmModule<T: Config> {
|
||||
pub code: Vec<u8>,
|
||||
pub hash: <T::Hashing as Hash>::Output,
|
||||
memory: Option<ImportedMemory>,
|
||||
@@ -136,27 +135,37 @@ where
|
||||
let mut contract = builder::module()
|
||||
// deploy function (first internal function)
|
||||
.function()
|
||||
.signature().build()
|
||||
.with_body(def.deploy_body.unwrap_or_else(||
|
||||
FuncBody::new(Vec::new(), Instructions::empty())
|
||||
))
|
||||
.build()
|
||||
.signature()
|
||||
.build()
|
||||
.with_body(
|
||||
def.deploy_body
|
||||
.unwrap_or_else(|| FuncBody::new(Vec::new(), Instructions::empty())),
|
||||
)
|
||||
.build()
|
||||
// call function (second internal function)
|
||||
.function()
|
||||
.signature().build()
|
||||
.with_body(def.call_body.unwrap_or_else(||
|
||||
FuncBody::new(Vec::new(), Instructions::empty())
|
||||
))
|
||||
.build()
|
||||
.export().field("deploy").internal().func(func_offset).build()
|
||||
.export().field("call").internal().func(func_offset + 1).build();
|
||||
.signature()
|
||||
.build()
|
||||
.with_body(
|
||||
def.call_body
|
||||
.unwrap_or_else(|| FuncBody::new(Vec::new(), Instructions::empty())),
|
||||
)
|
||||
.build()
|
||||
.export()
|
||||
.field("deploy")
|
||||
.internal()
|
||||
.func(func_offset)
|
||||
.build()
|
||||
.export()
|
||||
.field("call")
|
||||
.internal()
|
||||
.func(func_offset + 1)
|
||||
.build();
|
||||
|
||||
// If specified we add an additional internal function
|
||||
if let Some(body) = def.aux_body {
|
||||
let mut signature = contract
|
||||
.function()
|
||||
.signature();
|
||||
for _ in 0 .. def.aux_arg_num {
|
||||
let mut signature = contract.function().signature();
|
||||
for _ in 0..def.aux_arg_num {
|
||||
signature = signature.with_param(ValueType::I64);
|
||||
}
|
||||
contract = signature.build().with_body(body).build();
|
||||
@@ -164,9 +173,12 @@ where
|
||||
|
||||
// Grant access to linear memory.
|
||||
if let Some(memory) = &def.memory {
|
||||
contract = contract.import()
|
||||
.module("env").field("memory")
|
||||
.external().memory(memory.min_pages, Some(memory.max_pages))
|
||||
contract = contract
|
||||
.import()
|
||||
.module("env")
|
||||
.field("memory")
|
||||
.external()
|
||||
.memory(memory.min_pages, Some(memory.max_pages))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -177,7 +189,8 @@ where
|
||||
.with_results(func.return_type.into_iter().collect())
|
||||
.build_sig();
|
||||
let sig = contract.push_signature(sig);
|
||||
contract = contract.import()
|
||||
contract = contract
|
||||
.import()
|
||||
.module(func.module)
|
||||
.field(func.name)
|
||||
.with_external(elements::External::Function(sig))
|
||||
@@ -186,7 +199,8 @@ where
|
||||
|
||||
// Initialize memory
|
||||
for data in def.data_segments {
|
||||
contract = contract.data()
|
||||
contract = contract
|
||||
.data()
|
||||
.offset(Instruction::I32Const(data.offset as i32))
|
||||
.value(data.value)
|
||||
.build()
|
||||
@@ -194,12 +208,13 @@ where
|
||||
|
||||
// Add global variables
|
||||
if def.num_globals > 0 {
|
||||
use rand::{prelude::*, distributions::Standard};
|
||||
use rand::{distributions::Standard, prelude::*};
|
||||
let rng = rand_pcg::Pcg32::seed_from_u64(3112244599778833558);
|
||||
for val in rng.sample_iter(Standard).take(def.num_globals as usize) {
|
||||
contract = contract
|
||||
.global()
|
||||
.value_type().i64()
|
||||
.value_type()
|
||||
.i64()
|
||||
.mutable()
|
||||
.init_expr(Instruction::I64Const(val))
|
||||
.build()
|
||||
@@ -218,31 +233,22 @@ where
|
||||
|
||||
// Add the dummy section
|
||||
if def.dummy_section > 0 {
|
||||
contract = contract.with_section(
|
||||
Section::Custom(
|
||||
CustomSection::new("dummy".to_owned(), vec![42; def.dummy_section as usize])
|
||||
)
|
||||
);
|
||||
contract = contract.with_section(Section::Custom(CustomSection::new(
|
||||
"dummy".to_owned(),
|
||||
vec![42; def.dummy_section as usize],
|
||||
)));
|
||||
}
|
||||
|
||||
let mut code = contract.build();
|
||||
|
||||
// Inject stack height metering
|
||||
if def.inject_stack_metering {
|
||||
code = inject_limiter(
|
||||
code,
|
||||
T::Schedule::get().limits.stack_height
|
||||
)
|
||||
.unwrap();
|
||||
code = inject_limiter(code, T::Schedule::get().limits.stack_height).unwrap();
|
||||
}
|
||||
|
||||
let code = code.to_bytes().unwrap();
|
||||
let hash = T::Hashing::hash(&code);
|
||||
Self {
|
||||
code,
|
||||
hash,
|
||||
memory: def.memory,
|
||||
}
|
||||
Self { code, hash, memory: def.memory }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +272,7 @@ where
|
||||
ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
dummy_section: dummy_bytes.saturating_sub(module_overhead),
|
||||
.. Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -275,23 +281,18 @@ where
|
||||
/// `instantiate_with_code` for different sizes of wasm modules. The generated module maximizes
|
||||
/// instrumentation runtime by nesting blocks as deeply as possible given the byte budget.
|
||||
pub fn sized(target_bytes: u32) -> Self {
|
||||
use self::elements::Instruction::{If, I32Const, Return, End};
|
||||
use self::elements::Instruction::{End, I32Const, If, Return};
|
||||
// Base size of a contract is 63 bytes and each expansion adds 6 bytes.
|
||||
// We do one expansion less to account for the code section and function body
|
||||
// size fields inside the binary wasm module representation which are leb128 encoded
|
||||
// and therefore grow in size when the contract grows. We are not allowed to overshoot
|
||||
// because of the maximum code size that is enforced by `instantiate_with_code`.
|
||||
let expansions = (target_bytes.saturating_sub(63) / 6).saturating_sub(1);
|
||||
const EXPANSION: [Instruction; 4] = [
|
||||
I32Const(0),
|
||||
If(BlockType::NoResult),
|
||||
Return,
|
||||
End,
|
||||
];
|
||||
const EXPANSION: [Instruction; 4] = [I32Const(0), If(BlockType::NoResult), Return, End];
|
||||
ModuleDefinition {
|
||||
call_body: Some(body::repeated(expansions, &EXPANSION)),
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
.. Default::default()
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -317,12 +318,15 @@ where
|
||||
offset: 0,
|
||||
value: (pages * 64 * 1024 - 4).to_le_bytes().to_vec(),
|
||||
}],
|
||||
call_body: Some(body::repeated(repeat, &[
|
||||
Instruction::I32Const(4), // ptr where to store output
|
||||
Instruction::I32Const(0), // ptr to length
|
||||
Instruction::Call(0), // call the imported function
|
||||
])),
|
||||
.. Default::default()
|
||||
call_body: Some(body::repeated(
|
||||
repeat,
|
||||
&[
|
||||
Instruction::I32Const(4), // ptr where to store output
|
||||
Instruction::I32Const(0), // ptr to length
|
||||
Instruction::Call(0), // call the imported function
|
||||
],
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -339,13 +343,16 @@ where
|
||||
params: vec![ValueType::I32, ValueType::I32, ValueType::I32],
|
||||
return_type: None,
|
||||
}],
|
||||
call_body: Some(body::repeated(repeat, &[
|
||||
Instruction::I32Const(0), // input_ptr
|
||||
Instruction::I32Const(data_size as i32), // input_len
|
||||
Instruction::I32Const(0), // output_ptr
|
||||
Instruction::Call(0),
|
||||
])),
|
||||
.. Default::default()
|
||||
call_body: Some(body::repeated(
|
||||
repeat,
|
||||
&[
|
||||
Instruction::I32Const(0), // input_ptr
|
||||
Instruction::I32Const(data_size as i32), // input_len
|
||||
Instruction::I32Const(0), // output_ptr
|
||||
Instruction::Call(0),
|
||||
],
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -354,11 +361,7 @@ where
|
||||
/// and adds it to `env`. A reference to that memory is returned so that it can be used to
|
||||
/// access the memory contents from the supervisor.
|
||||
pub fn add_memory<S>(&self, env: &mut EnvironmentDefinitionBuilder<S>) -> Option<Memory> {
|
||||
let memory = if let Some(memory) = &self.memory {
|
||||
memory
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let memory = if let Some(memory) = &self.memory { memory } else { return None };
|
||||
let memory = Memory::new(memory.min_pages, Some(memory.max_pages)).unwrap();
|
||||
env.add_memory("env", "memory", memory.clone());
|
||||
Some(memory)
|
||||
@@ -367,25 +370,25 @@ where
|
||||
pub fn unary_instr(instr: Instruction, repeat: u32) -> Self {
|
||||
use body::DynInstr::{RandomI64Repeated, Regular};
|
||||
ModuleDefinition {
|
||||
call_body: Some(body::repeated_dyn(repeat, vec![
|
||||
RandomI64Repeated(1),
|
||||
Regular(instr),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
.. Default::default()
|
||||
}.into()
|
||||
call_body: Some(body::repeated_dyn(
|
||||
repeat,
|
||||
vec![RandomI64Repeated(1), Regular(instr), Regular(Instruction::Drop)],
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn binary_instr(instr: Instruction, repeat: u32) -> Self {
|
||||
use body::DynInstr::{RandomI64Repeated, Regular};
|
||||
ModuleDefinition {
|
||||
call_body: Some(body::repeated_dyn(repeat, vec![
|
||||
RandomI64Repeated(2),
|
||||
Regular(instr),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
.. Default::default()
|
||||
}.into()
|
||||
call_body: Some(body::repeated_dyn(
|
||||
repeat,
|
||||
vec![RandomI64Repeated(2), Regular(instr), Regular(Instruction::Drop)],
|
||||
)),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +429,7 @@ pub mod body {
|
||||
RandomGetGlobal(u32, u32),
|
||||
/// Insert a SetGlobal with a random offset in [low, high).
|
||||
/// (low, high)
|
||||
RandomSetGlobal(u32, u32)
|
||||
RandomSetGlobal(u32, u32),
|
||||
}
|
||||
|
||||
pub fn plain(instructions: Vec<Instruction>) -> FuncBody {
|
||||
@@ -441,13 +444,13 @@ pub mod body {
|
||||
.take(instructions.len() * usize::try_from(repetitions).unwrap())
|
||||
.cloned()
|
||||
.chain(sp_std::iter::once(Instruction::End))
|
||||
.collect()
|
||||
.collect(),
|
||||
);
|
||||
FuncBody::new(Vec::new(), instructions)
|
||||
}
|
||||
|
||||
pub fn repeated_dyn(repetitions: u32, mut instructions: Vec<DynInstr>) -> FuncBody {
|
||||
use rand::{prelude::*, distributions::Standard};
|
||||
use rand::{distributions::Standard, prelude::*};
|
||||
|
||||
// We do not need to be secure here.
|
||||
let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615);
|
||||
@@ -456,50 +459,46 @@ pub mod body {
|
||||
let body = (0..instructions.len())
|
||||
.cycle()
|
||||
.take(instructions.len() * usize::try_from(repetitions).unwrap())
|
||||
.flat_map(|idx|
|
||||
match &mut instructions[idx] {
|
||||
DynInstr::Regular(instruction) => vec![instruction.clone()],
|
||||
DynInstr::Counter(offset, increment_by) => {
|
||||
let current = *offset;
|
||||
*offset += *increment_by;
|
||||
vec![Instruction::I32Const(current as i32)]
|
||||
},
|
||||
DynInstr::RandomUnaligned(low, high) => {
|
||||
let unaligned = rng.gen_range(*low..*high) | 1;
|
||||
vec![Instruction::I32Const(unaligned as i32)]
|
||||
},
|
||||
DynInstr::RandomI32(low, high) => {
|
||||
vec![Instruction::I32Const(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomI32Repeated(num) => {
|
||||
(&mut rng).sample_iter(Standard).take(*num).map(|val|
|
||||
Instruction::I32Const(val)
|
||||
)
|
||||
.collect()
|
||||
},
|
||||
DynInstr::RandomI64Repeated(num) => {
|
||||
(&mut rng).sample_iter(Standard).take(*num).map(|val|
|
||||
Instruction::I64Const(val)
|
||||
)
|
||||
.collect()
|
||||
},
|
||||
DynInstr::RandomGetLocal(low, high) => {
|
||||
vec![Instruction::GetLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomSetLocal(low, high) => {
|
||||
vec![Instruction::SetLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomTeeLocal(low, high) => {
|
||||
vec![Instruction::TeeLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomGetGlobal(low, high) => {
|
||||
vec![Instruction::GetGlobal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomSetGlobal(low, high) => {
|
||||
vec![Instruction::SetGlobal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
}
|
||||
)
|
||||
.flat_map(|idx| match &mut instructions[idx] {
|
||||
DynInstr::Regular(instruction) => vec![instruction.clone()],
|
||||
DynInstr::Counter(offset, increment_by) => {
|
||||
let current = *offset;
|
||||
*offset += *increment_by;
|
||||
vec![Instruction::I32Const(current as i32)]
|
||||
},
|
||||
DynInstr::RandomUnaligned(low, high) => {
|
||||
let unaligned = rng.gen_range(*low..*high) | 1;
|
||||
vec![Instruction::I32Const(unaligned as i32)]
|
||||
},
|
||||
DynInstr::RandomI32(low, high) => {
|
||||
vec![Instruction::I32Const(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomI32Repeated(num) => (&mut rng)
|
||||
.sample_iter(Standard)
|
||||
.take(*num)
|
||||
.map(|val| Instruction::I32Const(val))
|
||||
.collect(),
|
||||
DynInstr::RandomI64Repeated(num) => (&mut rng)
|
||||
.sample_iter(Standard)
|
||||
.take(*num)
|
||||
.map(|val| Instruction::I64Const(val))
|
||||
.collect(),
|
||||
DynInstr::RandomGetLocal(low, high) => {
|
||||
vec![Instruction::GetLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomSetLocal(low, high) => {
|
||||
vec![Instruction::SetLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomTeeLocal(low, high) => {
|
||||
vec![Instruction::TeeLocal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomGetGlobal(low, high) => {
|
||||
vec![Instruction::GetGlobal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
DynInstr::RandomSetGlobal(low, high) => {
|
||||
vec![Instruction::SetGlobal(rng.gen_range(*low..*high))]
|
||||
},
|
||||
})
|
||||
.chain(sp_std::iter::once(Instruction::End))
|
||||
.collect();
|
||||
FuncBody::new(Vec::new(), Instructions::new(body))
|
||||
|
||||
@@ -22,28 +22,28 @@
|
||||
mod code;
|
||||
mod sandbox;
|
||||
|
||||
use self::{
|
||||
code::{
|
||||
body::{self, DynInstr::*},
|
||||
DataSegment, ImportedFunction, ImportedMemory, ModuleDefinition, WasmModule,
|
||||
},
|
||||
sandbox::Sandbox,
|
||||
};
|
||||
use crate::{
|
||||
*, Pallet as Contracts,
|
||||
exec::StorageKey,
|
||||
rent::Rent,
|
||||
schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE},
|
||||
storage::Storage,
|
||||
};
|
||||
use self::{
|
||||
code::{
|
||||
body::{self, DynInstr::*},
|
||||
ModuleDefinition, DataSegment, ImportedMemory, ImportedFunction, WasmModule,
|
||||
},
|
||||
sandbox::Sandbox,
|
||||
Pallet as Contracts, *,
|
||||
};
|
||||
use codec::Encode;
|
||||
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
|
||||
use frame_system::{Pallet as System, RawOrigin};
|
||||
use pwasm_utils::parity_wasm::elements::{Instruction, ValueType, BlockType, BrTableData};
|
||||
use sp_runtime::traits::{Hash, Bounded, Zero};
|
||||
use sp_std::{default::Default, convert::{TryInto}, vec::Vec, vec};
|
||||
use pallet_contracts_primitives::RentProjection;
|
||||
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller};
|
||||
use frame_support::weights::Weight;
|
||||
use frame_system::{Pallet as System, RawOrigin};
|
||||
use pallet_contracts_primitives::RentProjection;
|
||||
use pwasm_utils::parity_wasm::elements::{BlockType, BrTableData, Instruction, ValueType};
|
||||
use sp_runtime::traits::{Bounded, Hash, Zero};
|
||||
use sp_std::{convert::TryInto, default::Default, vec, vec::Vec};
|
||||
|
||||
/// How many batches we do per API benchmark.
|
||||
const API_BENCHMARK_BATCHES: u32 = 20;
|
||||
@@ -74,7 +74,7 @@ impl Endow {
|
||||
/// The maximum amount of balance a caller can transfer without being brought below
|
||||
/// the existential deposit. This assumes that every caller is funded with the amount
|
||||
/// returned by `caller_funding`.
|
||||
fn max<T:Config>() -> BalanceOf<T> {
|
||||
fn max<T: Config>() -> BalanceOf<T> {
|
||||
caller_funding::<T>().saturating_sub(T::Currency::minimum_balance())
|
||||
}
|
||||
}
|
||||
@@ -109,8 +109,7 @@ where
|
||||
module: WasmModule<T>,
|
||||
data: Vec<u8>,
|
||||
endowment: Endow,
|
||||
) -> Result<Contract<T>, &'static str>
|
||||
{
|
||||
) -> Result<Contract<T>, &'static str> {
|
||||
let (storage_size, endowment) = match endowment {
|
||||
Endow::CollectRent => {
|
||||
// storage_size cannot be zero because otherwise a contract that is just above
|
||||
@@ -182,7 +181,8 @@ where
|
||||
|
||||
/// Get the `AliveContractInfo` of the `addr` or an error if it is no longer alive.
|
||||
fn address_alive_info(addr: &T::AccountId) -> Result<AliveContractInfo<T>, &'static str> {
|
||||
ContractInfoOf::<T>::get(addr).and_then(|c| c.get_alive())
|
||||
ContractInfoOf::<T>::get(addr)
|
||||
.and_then(|c| c.get_alive())
|
||||
.ok_or("Expected contract to be alive at this point.")
|
||||
}
|
||||
|
||||
@@ -193,7 +193,8 @@ where
|
||||
|
||||
/// Return an error if this contract is no tombstone.
|
||||
fn ensure_tombstone(&self) -> Result<(), &'static str> {
|
||||
ContractInfoOf::<T>::get(&self.account_id).and_then(|c| c.get_tombstone())
|
||||
ContractInfoOf::<T>::get(&self.account_id)
|
||||
.and_then(|c| c.get_tombstone())
|
||||
.ok_or("Expected contract to be a tombstone at this point.")
|
||||
.map(|_| ())
|
||||
}
|
||||
@@ -236,16 +237,13 @@ where
|
||||
let contract = Contract::<T>::new(code, vec![], Endow::CollectRent)?;
|
||||
let storage_items = create_storage::<T>(stor_num, stor_size)?;
|
||||
contract.store(&storage_items)?;
|
||||
Ok(Self {
|
||||
contract,
|
||||
storage: storage_items,
|
||||
})
|
||||
Ok(Self { contract, storage: storage_items })
|
||||
}
|
||||
|
||||
/// Increase the system block number so that this contract is eligible for eviction.
|
||||
fn set_block_num_for_eviction(&self) -> Result<(), &'static str> {
|
||||
fn set_block_num_for_eviction(&self) -> Result<(), &'static str> {
|
||||
System::<T>::set_block_number(
|
||||
self.contract.eviction_at()? + T::SignedClaimHandicap::get() + 5u32.into()
|
||||
self.contract.eviction_at()? + T::SignedClaimHandicap::get() + 5u32.into(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -261,15 +259,17 @@ where
|
||||
/// Generate `stor_num` storage items. Each has the size `stor_size`.
|
||||
fn create_storage<T: Config>(
|
||||
stor_num: u32,
|
||||
stor_size: u32
|
||||
stor_size: u32,
|
||||
) -> Result<Vec<(StorageKey, Vec<u8>)>, &'static str> {
|
||||
(0..stor_num).map(|i| {
|
||||
let hash = T::Hashing::hash_of(&i)
|
||||
.as_ref()
|
||||
.try_into()
|
||||
.map_err(|_| "Hash too big for storage key")?;
|
||||
Ok((hash, vec![42u8; stor_size as usize]))
|
||||
}).collect::<Result<Vec<_>, &'static str>>()
|
||||
(0..stor_num)
|
||||
.map(|i| {
|
||||
let hash = T::Hashing::hash_of(&i)
|
||||
.as_ref()
|
||||
.try_into()
|
||||
.map_err(|_| "Hash too big for storage key")?;
|
||||
Ok((hash, vec![42u8; stor_size as usize]))
|
||||
})
|
||||
.collect::<Result<Vec<_>, &'static str>>()
|
||||
}
|
||||
|
||||
/// The funding that each account that either calls or instantiates contracts is funded with.
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
///! For instruction benchmarking we do no instantiate a full contract but merely the
|
||||
///! sandbox to execute the wasm code. This is because we do not need the full
|
||||
///! environment that provides the seal interface as imported functions.
|
||||
|
||||
use super::{
|
||||
Config,
|
||||
code::WasmModule,
|
||||
};
|
||||
/// ! For instruction benchmarking we do no instantiate a full contract but merely the
|
||||
/// ! sandbox to execute the wasm code. This is because we do not need the full
|
||||
/// ! environment that provides the seal interface as imported functions.
|
||||
use super::{code::WasmModule, Config};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_sandbox::{EnvironmentDefinitionBuilder, Instance, Memory};
|
||||
|
||||
@@ -51,9 +47,6 @@ where
|
||||
let memory = module.add_memory(&mut env_builder);
|
||||
let instance = Instance::new(&module.code, &env_builder, &mut ())
|
||||
.expect("Failed to create benchmarking Sandbox instance");
|
||||
Self {
|
||||
instance,
|
||||
_memory: memory,
|
||||
}
|
||||
Self { instance, _memory: memory }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,22 +55,19 @@
|
||||
//! on how to use a chain extension in order to provide new features to ink! contracts.
|
||||
|
||||
use crate::{
|
||||
Error,
|
||||
wasm::{Runtime, RuntimeCosts},
|
||||
gas::ChargedAmount,
|
||||
wasm::{Runtime, RuntimeCosts},
|
||||
Error,
|
||||
};
|
||||
use codec::{Decode, MaxEncodedLen};
|
||||
use frame_support::weights::Weight;
|
||||
use sp_runtime::DispatchError;
|
||||
use sp_std::{
|
||||
marker::PhantomData,
|
||||
vec::Vec,
|
||||
};
|
||||
use sp_std::{marker::PhantomData, vec::Vec};
|
||||
|
||||
pub use crate::{exec::Ext, Config};
|
||||
pub use frame_system::Config as SysConfig;
|
||||
pub use pallet_contracts_primitives::ReturnFlags;
|
||||
pub use sp_core::crypto::UncheckedFrom;
|
||||
pub use crate::{Config, exec::Ext};
|
||||
pub use state::Init as InitState;
|
||||
|
||||
/// Result that returns a [`DispatchError`] on error.
|
||||
@@ -90,7 +87,7 @@ pub trait ChainExtension<C: Config> {
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `func_id`: The first argument to `seal_call_chain_extension`. Usually used to
|
||||
/// determine which function to realize.
|
||||
/// determine which function to realize.
|
||||
/// - `env`: Access to the remaining arguments and the execution environment.
|
||||
///
|
||||
/// # Return
|
||||
@@ -143,7 +140,7 @@ pub enum RetVal {
|
||||
/// The semantic is the same as for calling `seal_return`: The control returns to
|
||||
/// the caller of the currently executing contract yielding the supplied buffer and
|
||||
/// flags.
|
||||
Diverging{flags: ReturnFlags, data: Vec<u8>},
|
||||
Diverging { flags: ReturnFlags, data: Vec<u8> },
|
||||
}
|
||||
|
||||
/// Grants the chain extension access to its parameters and execution environment.
|
||||
@@ -183,7 +180,9 @@ where
|
||||
/// This is when a maximum a priori amount was charged and then should be partially
|
||||
/// refunded to match the actual amount.
|
||||
pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) {
|
||||
self.inner.runtime.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight))
|
||||
self.inner
|
||||
.runtime
|
||||
.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight))
|
||||
}
|
||||
|
||||
/// Grants access to the execution environment of the current contract call.
|
||||
@@ -204,46 +203,31 @@ impl<'a, 'b, E: Ext> Environment<'a, 'b, E, state::Init> {
|
||||
/// It is only available to this crate because only the wasm runtime module needs to
|
||||
/// ever create this type. Chain extensions merely consume it.
|
||||
pub(crate) fn new(
|
||||
runtime: &'a mut Runtime::<'b, E>,
|
||||
runtime: &'a mut Runtime<'b, E>,
|
||||
input_ptr: u32,
|
||||
input_len: u32,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32,
|
||||
) -> Self {
|
||||
Environment {
|
||||
inner: Inner {
|
||||
runtime,
|
||||
input_ptr,
|
||||
input_len,
|
||||
output_ptr,
|
||||
output_len_ptr,
|
||||
},
|
||||
inner: Inner { runtime, input_ptr, input_len, output_ptr, output_len_ptr },
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Use all arguments as integer values.
|
||||
pub fn only_in(self) -> Environment<'a, 'b, E, state::OnlyIn> {
|
||||
Environment {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
Environment { inner: self.inner, phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// Use input arguments as integer and output arguments as pointer to a buffer.
|
||||
pub fn prim_in_buf_out(self) -> Environment<'a, 'b, E, state::PrimInBufOut> {
|
||||
Environment {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
Environment { inner: self.inner, phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// Use input and output arguments as pointers to a buffer.
|
||||
pub fn buf_in_buf_out(self) -> Environment<'a, 'b, E, state::BufInBufOut> {
|
||||
Environment {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
Environment { inner: self.inner, phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,10 +271,9 @@ where
|
||||
/// charge the overall costs either using `max_len` (worst case approximation) or using
|
||||
/// [`in_len()`](Self::in_len).
|
||||
pub fn read(&self, max_len: u32) -> Result<Vec<u8>> {
|
||||
self.inner.runtime.read_sandbox_memory(
|
||||
self.inner.input_ptr,
|
||||
self.inner.input_len.min(max_len),
|
||||
)
|
||||
self.inner
|
||||
.runtime
|
||||
.read_sandbox_memory(self.inner.input_ptr, self.inner.input_len.min(max_len))
|
||||
}
|
||||
|
||||
/// Reads `min(buffer.len(), in_len) from contract memory.
|
||||
@@ -304,10 +287,7 @@ where
|
||||
let buffer = core::mem::take(buffer);
|
||||
&mut buffer[..len.min(self.inner.input_len as usize)]
|
||||
};
|
||||
self.inner.runtime.read_sandbox_memory_into_buf(
|
||||
self.inner.input_ptr,
|
||||
sliced,
|
||||
)?;
|
||||
self.inner.runtime.read_sandbox_memory_into_buf(self.inner.input_ptr, sliced)?;
|
||||
*buffer = sliced;
|
||||
Ok(())
|
||||
}
|
||||
@@ -377,7 +357,7 @@ where
|
||||
/// gets too large.
|
||||
struct Inner<'a, 'b, E: Ext> {
|
||||
/// The runtime contains all necessary functions to interact with the running contract.
|
||||
runtime: &'a mut Runtime::<'b, E>,
|
||||
runtime: &'a mut Runtime<'b, E>,
|
||||
/// Verbatim argument passed to `seal_call_chain_extension`.
|
||||
input_ptr: u32,
|
||||
/// Verbatim argument passed to `seal_call_chain_extension`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,17 +15,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{Config, Error, exec::ExecError};
|
||||
use sp_std::marker::PhantomData;
|
||||
use sp_runtime::traits::Zero;
|
||||
use crate::{exec::ExecError, Config, Error};
|
||||
use frame_support::{
|
||||
dispatch::{
|
||||
DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo, DispatchError,
|
||||
DispatchError, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, PostDispatchInfo,
|
||||
},
|
||||
weights::Weight,
|
||||
DefaultNoBound,
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_runtime::traits::Zero;
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
#[cfg(test)]
|
||||
use std::{any::Any, fmt::Debug};
|
||||
@@ -88,7 +88,7 @@ pub struct GasMeter<T: Config> {
|
||||
|
||||
impl<T: Config> GasMeter<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<<T as frame_system::Config>::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<<T as frame_system::Config>::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
pub fn new(gas_limit: Weight) -> Self {
|
||||
GasMeter {
|
||||
@@ -107,11 +107,7 @@ where
|
||||
///
|
||||
/// Passing `0` as amount is interpreted as "all remaining gas".
|
||||
pub fn nested(&mut self, amount: Weight) -> Result<Self, DispatchError> {
|
||||
let amount = if amount == 0 {
|
||||
self.gas_left
|
||||
} else {
|
||||
amount
|
||||
};
|
||||
let amount = if amount == 0 { self.gas_left } else { amount };
|
||||
|
||||
// NOTE that it is ok to allocate all available gas since it still ensured
|
||||
// by `charge` that it doesn't reach zero.
|
||||
@@ -155,10 +151,8 @@ where
|
||||
#[cfg(test)]
|
||||
{
|
||||
// Unconditionally add the token to the storage.
|
||||
let erased_tok = ErasedToken {
|
||||
description: format!("{:?}", token),
|
||||
token: Box::new(token),
|
||||
};
|
||||
let erased_tok =
|
||||
ErasedToken { description: format!("{:?}", token), token: Box::new(token) };
|
||||
self.tokens.push(erased_tok);
|
||||
}
|
||||
|
||||
@@ -277,7 +271,9 @@ mod tests {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct SimpleToken(u64);
|
||||
impl Token<Test> for SimpleToken {
|
||||
fn weight(&self) -> u64 { self.0 }
|
||||
fn weight(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -318,7 +314,6 @@ mod tests {
|
||||
assert!(gas_meter.charge(SimpleToken(1)).is_err());
|
||||
}
|
||||
|
||||
|
||||
// Charging the exact amount that the user paid for should be
|
||||
// possible.
|
||||
#[test]
|
||||
|
||||
@@ -78,17 +78,17 @@
|
||||
//! WebAssembly based smart contracts in the Rust programming language. This is a work in progress.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "runtime-benchmarks", recursion_limit="512")]
|
||||
#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "512")]
|
||||
|
||||
#[macro_use]
|
||||
mod gas;
|
||||
mod storage;
|
||||
mod exec;
|
||||
mod wasm;
|
||||
mod rent;
|
||||
mod benchmarking;
|
||||
mod schedule;
|
||||
mod exec;
|
||||
mod migration;
|
||||
mod rent;
|
||||
mod schedule;
|
||||
mod storage;
|
||||
mod wasm;
|
||||
|
||||
pub mod chain_extension;
|
||||
pub mod weights;
|
||||
@@ -97,49 +97,48 @@ pub mod weights;
|
||||
mod tests;
|
||||
|
||||
pub use crate::{
|
||||
pallet::*,
|
||||
schedule::{Schedule, Limits, InstructionWeights, HostFnWeights},
|
||||
exec::Frame,
|
||||
pallet::*,
|
||||
schedule::{HostFnWeights, InstructionWeights, Limits, Schedule},
|
||||
};
|
||||
use crate::{
|
||||
exec::{Executable, Stack as ExecStack},
|
||||
gas::GasMeter,
|
||||
exec::{Stack as ExecStack, Executable},
|
||||
rent::Rent,
|
||||
storage::{Storage, DeletedContract, ContractInfo, AliveContractInfo, TombstoneContractInfo},
|
||||
weights::WeightInfo,
|
||||
storage::{AliveContractInfo, ContractInfo, DeletedContract, Storage, TombstoneContractInfo},
|
||||
wasm::PrefabWasmModule,
|
||||
};
|
||||
use sp_core::{Bytes, crypto::UncheckedFrom};
|
||||
use sp_std::prelude::*;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
Hash, StaticLookup, Convert, Saturating, Zero,
|
||||
},
|
||||
Perbill,
|
||||
weights::WeightInfo,
|
||||
};
|
||||
use frame_support::{
|
||||
traits::{OnUnbalanced, Currency, Get, Time, Randomness, Filter},
|
||||
weights::{Weight, PostDispatchInfo, WithPostDispatchInfo, GetDispatchInfo},
|
||||
dispatch::Dispatchable,
|
||||
traits::{Currency, Filter, Get, OnUnbalanced, Randomness, Time},
|
||||
weights::{GetDispatchInfo, PostDispatchInfo, Weight, WithPostDispatchInfo},
|
||||
};
|
||||
use frame_system::Pallet as System;
|
||||
use pallet_contracts_primitives::{
|
||||
RentProjectionResult, GetStorageResult, ContractAccessError, ContractExecResult,
|
||||
ContractInstantiateResult, Code, InstantiateReturnValue,
|
||||
Code, ContractAccessError, ContractExecResult, ContractInstantiateResult, GetStorageResult,
|
||||
InstantiateReturnValue, RentProjectionResult,
|
||||
};
|
||||
use sp_core::{crypto::UncheckedFrom, Bytes};
|
||||
use sp_runtime::{
|
||||
traits::{Convert, Hash, Saturating, StaticLookup, Zero},
|
||||
Perbill,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
type CodeHash<T> = <T as frame_system::Config>::Hash;
|
||||
type TrieId = Vec<u8>;
|
||||
type BalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||
type NegativeImbalanceOf<T> =
|
||||
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||
type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
>>::NegativeImbalance;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
@@ -156,11 +155,10 @@ pub mod pallet {
|
||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||
|
||||
/// The overarching call type.
|
||||
type Call:
|
||||
Dispatchable<Origin=Self::Origin, PostInfo=PostDispatchInfo> +
|
||||
GetDispatchInfo +
|
||||
codec::Decode +
|
||||
IsType<<Self as frame_system::Config>::Call>;
|
||||
type Call: Dispatchable<Origin = Self::Origin, PostInfo = PostDispatchInfo>
|
||||
+ GetDispatchInfo
|
||||
+ codec::Decode
|
||||
+ IsType<<Self as frame_system::Config>::Call>;
|
||||
|
||||
/// Filter that is applied to calls dispatched by contracts.
|
||||
///
|
||||
@@ -263,7 +261,7 @@ pub mod pallet {
|
||||
/// The allowed depth is `CallStack::size() + 1`.
|
||||
/// Therefore a size of `0` means that a contract cannot use call or instantiate.
|
||||
/// In other words only the origin called "root contract" is allowed to execute then.
|
||||
type CallStack: smallvec::Array<Item=Frame<Self>>;
|
||||
type CallStack: smallvec::Array<Item = Frame<Self>>;
|
||||
|
||||
/// The maximum number of tries that can be queued for deletion.
|
||||
#[pallet::constant]
|
||||
@@ -286,7 +284,8 @@ pub mod pallet {
|
||||
fn on_initialize(_block: T::BlockNumber) -> Weight {
|
||||
// We do not want to go above the block limit and rather avoid lazy deletion
|
||||
// in that case. This should only happen on runtime upgrades.
|
||||
let weight_limit = T::BlockWeights::get().max_block
|
||||
let weight_limit = T::BlockWeights::get()
|
||||
.max_block
|
||||
.saturating_sub(System::<T>::block_weight().total())
|
||||
.min(T::DeletionWeightLimit::get());
|
||||
Storage::<T>::process_deletion_queue_batch(weight_limit)
|
||||
@@ -317,14 +316,20 @@ pub mod pallet {
|
||||
dest: <T::Lookup as StaticLookup>::Source,
|
||||
#[pallet::compact] value: BalanceOf<T>,
|
||||
#[pallet::compact] gas_limit: Weight,
|
||||
data: Vec<u8>
|
||||
data: Vec<u8>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = ensure_signed(origin)?;
|
||||
let dest = T::Lookup::lookup(dest)?;
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = T::Schedule::get();
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||
origin, dest, &mut gas_meter, &schedule, value, data, None,
|
||||
origin,
|
||||
dest,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
value,
|
||||
data,
|
||||
None,
|
||||
);
|
||||
gas_meter.into_dispatch_result(result, T::WeightInfo::call())
|
||||
}
|
||||
@@ -374,11 +379,19 @@ pub mod pallet {
|
||||
let code_len = executable.code_len();
|
||||
ensure!(code_len <= T::Schedule::get().limits.code_len, Error::<T>::CodeTooLarge);
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt, None,
|
||||
).map(|(_address, output)| output);
|
||||
origin,
|
||||
executable,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
endowment,
|
||||
data,
|
||||
&salt,
|
||||
None,
|
||||
)
|
||||
.map(|(_address, output)| output);
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024)
|
||||
T::WeightInfo::instantiate_with_code(code_len / 1024, salt.len() as u32 / 1024),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -403,12 +416,18 @@ pub mod pallet {
|
||||
let schedule = T::Schedule::get();
|
||||
let executable = PrefabWasmModule::from_storage(code_hash, &schedule, &mut gas_meter)?;
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule, endowment, data, &salt, None,
|
||||
).map(|(_address, output)| output);
|
||||
gas_meter.into_dispatch_result(
|
||||
result,
|
||||
T::WeightInfo::instantiate(salt.len() as u32 / 1024),
|
||||
origin,
|
||||
executable,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
endowment,
|
||||
data,
|
||||
&salt,
|
||||
None,
|
||||
)
|
||||
.map(|(_address, output)| output);
|
||||
gas_meter
|
||||
.into_dispatch_result(result, T::WeightInfo::instantiate(salt.len() as u32 / 1024))
|
||||
}
|
||||
|
||||
/// Allows block producers to claim a small reward for evicting a contract. If a block
|
||||
@@ -424,44 +443,33 @@ pub mod pallet {
|
||||
pub fn claim_surcharge(
|
||||
origin: OriginFor<T>,
|
||||
dest: T::AccountId,
|
||||
aux_sender: Option<T::AccountId>
|
||||
aux_sender: Option<T::AccountId>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let origin = origin.into();
|
||||
let (signed, rewarded) = match (origin, aux_sender) {
|
||||
(Ok(frame_system::RawOrigin::Signed(account)), None) => {
|
||||
(true, account)
|
||||
},
|
||||
(Ok(frame_system::RawOrigin::None), Some(aux_sender)) => {
|
||||
(false, aux_sender)
|
||||
},
|
||||
(Ok(frame_system::RawOrigin::Signed(account)), None) => (true, account),
|
||||
(Ok(frame_system::RawOrigin::None), Some(aux_sender)) => (false, aux_sender),
|
||||
_ => Err(Error::<T>::InvalidSurchargeClaim)?,
|
||||
};
|
||||
|
||||
// Add some advantage for block producers (who send unsigned extrinsics) by
|
||||
// adding a handicap: for signed extrinsics we use a slightly older block number
|
||||
// for the eviction check. This can be viewed as if we pushed regular users back in past.
|
||||
let handicap = if signed {
|
||||
T::SignedClaimHandicap::get()
|
||||
} else {
|
||||
Zero::zero()
|
||||
};
|
||||
let handicap = if signed { T::SignedClaimHandicap::get() } else { Zero::zero() };
|
||||
|
||||
// If poking the contract has lead to eviction of the contract, give out the rewards.
|
||||
match Rent::<T, PrefabWasmModule<T>>::try_eviction(&dest, handicap)? {
|
||||
(Some(rent_paid), code_len) => {
|
||||
T::Currency::deposit_into_existing(
|
||||
&rewarded,
|
||||
T::SurchargeReward::get().min(rent_paid),
|
||||
)
|
||||
.map(|_| PostDispatchInfo {
|
||||
actual_weight: Some(T::WeightInfo::claim_surcharge(code_len / 1024)),
|
||||
pays_fee: Pays::No,
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
(None, code_len) => Err(Error::<T>::ContractNotEvictable.with_weight(
|
||||
T::WeightInfo::claim_surcharge(code_len / 1024)
|
||||
)),
|
||||
(Some(rent_paid), code_len) => T::Currency::deposit_into_existing(
|
||||
&rewarded,
|
||||
T::SurchargeReward::get().min(rent_paid),
|
||||
)
|
||||
.map(|_| PostDispatchInfo {
|
||||
actual_weight: Some(T::WeightInfo::claim_surcharge(code_len / 1024)),
|
||||
pays_fee: Pays::No,
|
||||
})
|
||||
.map_err(Into::into),
|
||||
(None, code_len) => Err(Error::<T>::ContractNotEvictable
|
||||
.with_weight(T::WeightInfo::claim_surcharge(code_len / 1024))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,7 +646,8 @@ pub mod pallet {
|
||||
|
||||
/// A mapping between an original code hash and instrumented wasm code, ready for execution.
|
||||
#[pallet::storage]
|
||||
pub(crate) type CodeStorage<T: Config> = StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;
|
||||
pub(crate) type CodeStorage<T: Config> =
|
||||
StorageMap<_, Identity, CodeHash<T>, PrefabWasmModule<T>>;
|
||||
|
||||
/// The subtrie counter.
|
||||
#[pallet::storage]
|
||||
@@ -648,7 +657,8 @@ pub mod pallet {
|
||||
///
|
||||
/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
|
||||
#[pallet::storage]
|
||||
pub(crate) type ContractInfoOf<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
|
||||
pub(crate) type ContractInfoOf<T: Config> =
|
||||
StorageMap<_, Twox64Concat, T::AccountId, ContractInfo<T>>;
|
||||
|
||||
/// Evicted contracts that await child trie deletion.
|
||||
///
|
||||
@@ -684,13 +694,15 @@ where
|
||||
) -> ContractExecResult {
|
||||
let mut gas_meter = GasMeter::new(gas_limit);
|
||||
let schedule = T::Schedule::get();
|
||||
let mut debug_message = if debug {
|
||||
Some(Vec::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut debug_message = if debug { Some(Vec::new()) } else { None };
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_call(
|
||||
origin, dest, &mut gas_meter, &schedule, value, input_data, debug_message.as_mut(),
|
||||
origin,
|
||||
dest,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
value,
|
||||
input_data,
|
||||
debug_message.as_mut(),
|
||||
);
|
||||
ContractExecResult {
|
||||
result: result.map_err(|r| r.error),
|
||||
@@ -734,34 +746,36 @@ where
|
||||
};
|
||||
let executable = match executable {
|
||||
Ok(executable) => executable,
|
||||
Err(error) => return ContractInstantiateResult {
|
||||
result: Err(error.into()),
|
||||
gas_consumed: gas_meter.gas_consumed(),
|
||||
gas_required: gas_meter.gas_required(),
|
||||
debug_message: Vec::new(),
|
||||
}
|
||||
};
|
||||
let mut debug_message = if debug {
|
||||
Some(Vec::new())
|
||||
} else {
|
||||
None
|
||||
Err(error) =>
|
||||
return ContractInstantiateResult {
|
||||
result: Err(error.into()),
|
||||
gas_consumed: gas_meter.gas_consumed(),
|
||||
gas_required: gas_meter.gas_required(),
|
||||
debug_message: Vec::new(),
|
||||
},
|
||||
};
|
||||
let mut debug_message = if debug { Some(Vec::new()) } else { None };
|
||||
let result = ExecStack::<T, PrefabWasmModule<T>>::run_instantiate(
|
||||
origin, executable, &mut gas_meter, &schedule,
|
||||
endowment, data, &salt, debug_message.as_mut(),
|
||||
).and_then(|(account_id, result)| {
|
||||
origin,
|
||||
executable,
|
||||
&mut gas_meter,
|
||||
&schedule,
|
||||
endowment,
|
||||
data,
|
||||
&salt,
|
||||
debug_message.as_mut(),
|
||||
)
|
||||
.and_then(|(account_id, result)| {
|
||||
let rent_projection = if compute_projection {
|
||||
Some(Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
||||
.map_err(|_| <Error<T>>::NewContractNotFunded)?)
|
||||
Some(
|
||||
Rent::<T, PrefabWasmModule<T>>::compute_projection(&account_id)
|
||||
.map_err(|_| <Error<T>>::NewContractNotFunded)?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(InstantiateReturnValue {
|
||||
result,
|
||||
account_id,
|
||||
rent_projection,
|
||||
})
|
||||
Ok(InstantiateReturnValue { result, account_id, rent_projection })
|
||||
});
|
||||
ContractInstantiateResult {
|
||||
result: result.map_err(|e| e.error),
|
||||
@@ -800,9 +814,10 @@ where
|
||||
deploying_address: &T::AccountId,
|
||||
code_hash: &CodeHash<T>,
|
||||
salt: &[u8],
|
||||
) -> T::AccountId
|
||||
{
|
||||
let buf: Vec<_> = deploying_address.as_ref().iter()
|
||||
) -> T::AccountId {
|
||||
let buf: Vec<_> = deploying_address
|
||||
.as_ref()
|
||||
.iter()
|
||||
.chain(code_hash.as_ref())
|
||||
.chain(salt)
|
||||
.cloned()
|
||||
@@ -847,7 +862,7 @@ where
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn reinstrument_module(
|
||||
module: &mut PrefabWasmModule<T>,
|
||||
schedule: &Schedule<T>
|
||||
schedule: &Schedule<T>,
|
||||
) -> frame_support::dispatch::DispatchResult {
|
||||
self::wasm::reinstrument(module, schedule)
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{Config, Weight, Pallet};
|
||||
use crate::{Config, Pallet, Weight};
|
||||
use frame_support::{
|
||||
storage::migration,
|
||||
traits::{GetPalletVersion, PalletVersion, PalletInfoAccess, Get},
|
||||
traits::{Get, GetPalletVersion, PalletInfoAccess, PalletVersion},
|
||||
};
|
||||
|
||||
pub fn migrate<T: Config>() -> Weight {
|
||||
@@ -32,7 +32,7 @@ pub fn migrate<T: Config>() -> Weight {
|
||||
b"CurrentSchedule",
|
||||
b"",
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
||||
@@ -18,23 +18,23 @@
|
||||
//! A module responsible for computing the right amount of weight and charging it.
|
||||
|
||||
use crate::{
|
||||
AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Pallet, Event,
|
||||
TombstoneContractInfo, Config, CodeHash, Error,
|
||||
storage::Storage, wasm::PrefabWasmModule, exec::Executable, gas::GasMeter,
|
||||
exec::Executable, gas::GasMeter, storage::Storage, wasm::PrefabWasmModule, AliveContractInfo,
|
||||
BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, Error, Event, Pallet,
|
||||
TombstoneContractInfo,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use frame_support::{
|
||||
storage::child,
|
||||
traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReasons},
|
||||
DefaultNoBound,
|
||||
};
|
||||
use pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{
|
||||
DispatchError,
|
||||
traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero},
|
||||
DispatchError,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Information about the required deposit and resulting rent.
|
||||
///
|
||||
@@ -83,13 +83,8 @@ where
|
||||
code_size: u32,
|
||||
) -> Result<Option<AliveContractInfo<T>>, DispatchError> {
|
||||
let current_block_number = <frame_system::Pallet<T>>::block_number();
|
||||
let verdict = Self::consider_case(
|
||||
account,
|
||||
current_block_number,
|
||||
Zero::zero(),
|
||||
&contract,
|
||||
code_size,
|
||||
);
|
||||
let verdict =
|
||||
Self::consider_case(account, current_block_number, Zero::zero(), &contract, code_size);
|
||||
Self::enact_verdict(account, contract, current_block_number, verdict, None)
|
||||
}
|
||||
|
||||
@@ -136,10 +131,14 @@ where
|
||||
.unwrap_or_else(|| <BalanceOf<T>>::zero())
|
||||
.saturating_add(contract.rent_paid);
|
||||
Self::enact_verdict(
|
||||
account, contract, current_block_number, verdict, Some(module),
|
||||
account,
|
||||
contract,
|
||||
current_block_number,
|
||||
verdict,
|
||||
Some(module),
|
||||
)?;
|
||||
Ok((Some(rent_paid), code_len))
|
||||
}
|
||||
},
|
||||
_ => Ok((None, code_len)),
|
||||
}
|
||||
}
|
||||
@@ -155,9 +154,7 @@ where
|
||||
/// NOTE that this is not a side-effect free function! It will actually collect rent and then
|
||||
/// compute the projection. This function is only used for implementation of an RPC method through
|
||||
/// `RuntimeApi` meaning that the changes will be discarded anyway.
|
||||
pub fn compute_projection(
|
||||
account: &T::AccountId,
|
||||
) -> RentProjectionResult<T::BlockNumber> {
|
||||
pub fn compute_projection(account: &T::AccountId) -> RentProjectionResult<T::BlockNumber> {
|
||||
use ContractAccessError::IsTombstone;
|
||||
|
||||
let contract_info = <ContractInfoOf<T>>::get(account);
|
||||
@@ -179,45 +176,42 @@ where
|
||||
|
||||
// We skip the eviction in case one is in order.
|
||||
// Evictions should only be performed by [`try_eviction`].
|
||||
let new_contract_info = Self::enact_verdict(
|
||||
account, alive_contract_info, current_block_number, verdict, None,
|
||||
);
|
||||
let new_contract_info =
|
||||
Self::enact_verdict(account, alive_contract_info, current_block_number, verdict, None);
|
||||
|
||||
// Check what happened after enaction of the verdict.
|
||||
let alive_contract_info = new_contract_info.map_err(|_| IsTombstone)?.ok_or_else(|| IsTombstone)?;
|
||||
let alive_contract_info =
|
||||
new_contract_info.map_err(|_| IsTombstone)?.ok_or_else(|| IsTombstone)?;
|
||||
|
||||
// Compute how much would the fee per block be with the *updated* balance.
|
||||
let total_balance = T::Currency::total_balance(account);
|
||||
let free_balance = T::Currency::free_balance(account);
|
||||
let fee_per_block = Self::fee_per_block(
|
||||
&free_balance, &alive_contract_info, code_size,
|
||||
);
|
||||
let fee_per_block = Self::fee_per_block(&free_balance, &alive_contract_info, code_size);
|
||||
if fee_per_block.is_zero() {
|
||||
return Ok(RentProjection::NoEviction);
|
||||
return Ok(RentProjection::NoEviction)
|
||||
}
|
||||
|
||||
// Then compute how much the contract will sustain under these circumstances.
|
||||
let rent_budget = Self::rent_budget(&total_balance, &free_balance, &alive_contract_info).expect(
|
||||
"the contract exists and in the alive state;
|
||||
let rent_budget = Self::rent_budget(&total_balance, &free_balance, &alive_contract_info)
|
||||
.expect(
|
||||
"the contract exists and in the alive state;
|
||||
the updated balance must be greater than subsistence deposit;
|
||||
this function doesn't return `None`;
|
||||
qed
|
||||
",
|
||||
);
|
||||
);
|
||||
let blocks_left = match rent_budget.checked_div(&fee_per_block) {
|
||||
Some(blocks_left) => blocks_left,
|
||||
None => {
|
||||
// `fee_per_block` is not zero here, so `checked_div` can return `None` if
|
||||
// there is an overflow. This cannot happen with integers though. Return
|
||||
// `NoEviction` here just in case.
|
||||
return Ok(RentProjection::NoEviction);
|
||||
}
|
||||
return Ok(RentProjection::NoEviction)
|
||||
},
|
||||
};
|
||||
|
||||
let blocks_left = blocks_left.saturated_into::<u32>().into();
|
||||
Ok(RentProjection::EvictionAt(
|
||||
current_block_number + blocks_left,
|
||||
))
|
||||
Ok(RentProjection::EvictionAt(current_block_number + blocks_left))
|
||||
}
|
||||
|
||||
/// Restores the destination account using the origin as prototype.
|
||||
@@ -246,18 +240,15 @@ where
|
||||
let current_block = <frame_system::Pallet<T>>::block_number();
|
||||
|
||||
if origin_contract.last_write == Some(current_block) {
|
||||
return Err(Error::<T>::InvalidContractOrigin.into());
|
||||
return Err(Error::<T>::InvalidContractOrigin.into())
|
||||
}
|
||||
|
||||
let dest_tombstone = <ContractInfoOf<T>>::get(&dest)
|
||||
.and_then(|c| c.get_tombstone())
|
||||
.ok_or(Error::<T>::InvalidDestinationContract)?;
|
||||
|
||||
let last_write = if !delta.is_empty() {
|
||||
Some(current_block)
|
||||
} else {
|
||||
origin_contract.last_write
|
||||
};
|
||||
let last_write =
|
||||
if !delta.is_empty() { Some(current_block) } else { origin_contract.last_write };
|
||||
|
||||
// Fails if the code hash does not exist on chain
|
||||
E::add_user(code_hash, gas_meter)?;
|
||||
@@ -266,7 +257,8 @@ where
|
||||
// fail later due to tombstones not matching. This is because the restoration
|
||||
// is always called from a contract and therefore in a storage transaction.
|
||||
// The failure of this function will lead to this transaction's rollback.
|
||||
let bytes_taken: u32 = delta.iter()
|
||||
let bytes_taken: u32 = delta
|
||||
.iter()
|
||||
.filter_map(|key| {
|
||||
let key = blake2_256(key);
|
||||
child::get_raw(&child_trie_info, &key).map(|value| {
|
||||
@@ -284,21 +276,24 @@ where
|
||||
);
|
||||
|
||||
if tombstone != dest_tombstone {
|
||||
return Err(Error::<T>::InvalidTombstone.into());
|
||||
return Err(Error::<T>::InvalidTombstone.into())
|
||||
}
|
||||
|
||||
origin_contract.storage_size -= bytes_taken;
|
||||
|
||||
<ContractInfoOf<T>>::remove(&origin);
|
||||
E::remove_user(origin_contract.code_hash, gas_meter)?;
|
||||
<ContractInfoOf<T>>::insert(&dest, ContractInfo::Alive(AliveContractInfo::<T> {
|
||||
code_hash,
|
||||
rent_allowance,
|
||||
rent_paid: <BalanceOf<T>>::zero(),
|
||||
deduct_block: current_block,
|
||||
last_write,
|
||||
.. origin_contract
|
||||
}));
|
||||
<ContractInfoOf<T>>::insert(
|
||||
&dest,
|
||||
ContractInfo::Alive(AliveContractInfo::<T> {
|
||||
code_hash,
|
||||
rent_allowance,
|
||||
rent_paid: <BalanceOf<T>>::zero(),
|
||||
deduct_block: current_block,
|
||||
last_write,
|
||||
..origin_contract
|
||||
}),
|
||||
);
|
||||
|
||||
let origin_free_balance = T::Currency::free_balance(&origin);
|
||||
T::Currency::make_free_balance_be(&origin, <BalanceOf<T>>::zero());
|
||||
@@ -314,42 +309,34 @@ where
|
||||
current_refcount: u32,
|
||||
at_refcount: u32,
|
||||
) -> RentStatus<T> {
|
||||
let calc_share = |refcount: u32| {
|
||||
aggregated_code_size.checked_div(refcount).unwrap_or(0)
|
||||
};
|
||||
let calc_share = |refcount: u32| aggregated_code_size.checked_div(refcount).unwrap_or(0);
|
||||
let current_share = calc_share(current_refcount);
|
||||
let custom_share = calc_share(at_refcount);
|
||||
RentStatus {
|
||||
max_deposit: Self::required_deposit(contract, aggregated_code_size),
|
||||
current_deposit: Self::required_deposit(contract, current_share),
|
||||
custom_refcount_deposit:
|
||||
if at_refcount > 0 {
|
||||
Some(Self::required_deposit(contract, custom_share))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
custom_refcount_deposit: if at_refcount > 0 {
|
||||
Some(Self::required_deposit(contract, custom_share))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
max_rent: Self::fee_per_block(free_balance, contract, aggregated_code_size),
|
||||
current_rent: Self::fee_per_block(free_balance, contract, current_share),
|
||||
custom_refcount_rent:
|
||||
if at_refcount > 0 {
|
||||
Some(Self::fee_per_block(free_balance, contract, custom_share))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
custom_refcount_rent: if at_refcount > 0 {
|
||||
Some(Self::fee_per_block(free_balance, contract, custom_share))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_reserved: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns how much deposit is required to not pay rent.
|
||||
fn required_deposit(
|
||||
contract: &AliveContractInfo<T>,
|
||||
code_size_share: u32,
|
||||
) -> BalanceOf<T> {
|
||||
fn required_deposit(contract: &AliveContractInfo<T>, code_size_share: u32) -> BalanceOf<T> {
|
||||
T::DepositPerStorageByte::get()
|
||||
.saturating_mul(contract.storage_size.saturating_add(code_size_share).into())
|
||||
.saturating_add(
|
||||
T::DepositPerStorageItem::get()
|
||||
.saturating_mul(contract.pair_count.into())
|
||||
T::DepositPerStorageItem::get().saturating_mul(contract.pair_count.into()),
|
||||
)
|
||||
.saturating_add(T::DepositPerContract::get())
|
||||
}
|
||||
@@ -363,8 +350,8 @@ where
|
||||
contract: &AliveContractInfo<T>,
|
||||
code_size_share: u32,
|
||||
) -> BalanceOf<T> {
|
||||
let missing_deposit = Self::required_deposit(contract, code_size_share)
|
||||
.saturating_sub(*free_balance);
|
||||
let missing_deposit =
|
||||
Self::required_deposit(contract, code_size_share).saturating_sub(*free_balance);
|
||||
T::RentFraction::get().mul_ceil(missing_deposit)
|
||||
}
|
||||
|
||||
@@ -383,16 +370,13 @@ where
|
||||
// Reserved balance contributes towards the subsistence threshold to stay consistent
|
||||
// with the existential deposit where the reserved balance is also counted.
|
||||
if *total_balance < subsistence_threshold {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
// However, reserved balance cannot be charged so we need to use the free balance
|
||||
// to calculate the actual budget (which can be 0).
|
||||
let rent_allowed_to_charge = free_balance.saturating_sub(subsistence_threshold);
|
||||
Some(<BalanceOf<T>>::min(
|
||||
contract.rent_allowance,
|
||||
rent_allowed_to_charge,
|
||||
))
|
||||
Some(<BalanceOf<T>>::min(contract.rent_allowance, rent_allowed_to_charge))
|
||||
}
|
||||
|
||||
/// Consider the case for rent payment of the given account and returns a `Verdict`.
|
||||
@@ -414,7 +398,7 @@ where
|
||||
};
|
||||
if blocks_passed.is_zero() {
|
||||
// Rent has already been paid
|
||||
return Verdict::Exempt;
|
||||
return Verdict::Exempt
|
||||
}
|
||||
|
||||
let total_balance = T::Currency::total_balance(account);
|
||||
@@ -425,7 +409,7 @@ where
|
||||
if fee_per_block.is_zero() {
|
||||
// The rent deposit offset reduced the fee to 0. This means that the contract
|
||||
// gets the rent for free.
|
||||
return Verdict::Exempt;
|
||||
return Verdict::Exempt
|
||||
}
|
||||
|
||||
let rent_budget = match Self::rent_budget(&total_balance, &free_balance, contract) {
|
||||
@@ -443,7 +427,7 @@ where
|
||||
account,
|
||||
);
|
||||
0u32.into()
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let dues = fee_per_block
|
||||
@@ -469,18 +453,15 @@ where
|
||||
if insufficient_rent || !can_withdraw_rent {
|
||||
// The contract cannot afford the rent payment and has a balance above the subsistence
|
||||
// threshold, so it leaves a tombstone.
|
||||
let amount = if can_withdraw_rent {
|
||||
Some(OutstandingAmount::new(dues_limited))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return Verdict::Evict { amount };
|
||||
let amount =
|
||||
if can_withdraw_rent { Some(OutstandingAmount::new(dues_limited)) } else { None };
|
||||
return Verdict::Evict { amount }
|
||||
}
|
||||
|
||||
return Verdict::Charge {
|
||||
// We choose to use `dues_limited` here instead of `dues` just to err on the safer side.
|
||||
amount: OutstandingAmount::new(dues_limited),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Enacts the given verdict and returns the updated `ContractInfo`.
|
||||
@@ -511,9 +492,7 @@ where
|
||||
}
|
||||
|
||||
// Note: this operation is heavy.
|
||||
let child_storage_root = child::root(
|
||||
&alive_contract_info.child_trie_info(),
|
||||
);
|
||||
let child_storage_root = child::root(&alive_contract_info.child_trie_info());
|
||||
|
||||
let tombstone = <TombstoneContractInfo<T>>::new(
|
||||
&child_storage_root[..],
|
||||
@@ -524,11 +503,9 @@ where
|
||||
code.drop_from_storage();
|
||||
<Pallet<T>>::deposit_event(Event::Evicted(account.clone()));
|
||||
Ok(None)
|
||||
}
|
||||
(Verdict::Evict { amount: _ }, None) => {
|
||||
Ok(None)
|
||||
}
|
||||
(Verdict::Exempt, _) => {
|
||||
},
|
||||
(Verdict::Evict { amount: _ }, None) => Ok(None),
|
||||
(Verdict::Exempt, _) => {
|
||||
let contract = ContractInfo::Alive(AliveContractInfo::<T> {
|
||||
deduct_block: current_block_number,
|
||||
..alive_contract_info
|
||||
@@ -546,11 +523,9 @@ where
|
||||
<ContractInfoOf<T>>::insert(account, &contract);
|
||||
amount.withdraw(account);
|
||||
Ok(Some(contract.get_alive().expect("We just constructed it as alive. qed")))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// The amount to charge.
|
||||
@@ -596,9 +571,7 @@ enum Verdict<T: Config> {
|
||||
Exempt,
|
||||
/// The contract cannot afford payment within its rent budget so it gets evicted. However,
|
||||
/// because its balance is greater than the subsistence threshold it leaves a tombstone.
|
||||
Evict {
|
||||
amount: Option<OutstandingAmount<T>>,
|
||||
},
|
||||
Evict { amount: Option<OutstandingAmount<T>> },
|
||||
/// Everything is OK, we just only take some charge.
|
||||
Charge { amount: OutstandingAmount<T> },
|
||||
}
|
||||
|
||||
@@ -18,16 +18,16 @@
|
||||
//! This module contains the cost schedule and supporting code that constructs a
|
||||
//! sane default schedule from a `WeightInfo` implementation.
|
||||
|
||||
use crate::{Config, weights::WeightInfo};
|
||||
use crate::{weights::WeightInfo, Config};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{weights::Weight, DefaultNoBound};
|
||||
use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug};
|
||||
use frame_support::{DefaultNoBound, weights::Weight};
|
||||
use sp_std::{marker::PhantomData, vec::Vec};
|
||||
use codec::{Encode, Decode};
|
||||
use pwasm_utils::{parity_wasm::elements, rules};
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sp_runtime::RuntimeDebug;
|
||||
use sp_std::{marker::PhantomData, vec::Vec};
|
||||
|
||||
/// How many API calls are executed in a single batch. The reason for increasing the amount
|
||||
/// of API calls in batches (per benchmark component increase) is so that the linear regression
|
||||
@@ -50,18 +50,18 @@ pub const INSTR_BENCHMARK_BATCH_SIZE: u32 = 1_000;
|
||||
/// fn create_schedule<T: Config>() -> Schedule<T> {
|
||||
/// Schedule {
|
||||
/// limits: Limits {
|
||||
/// globals: 3,
|
||||
/// parameters: 3,
|
||||
/// memory_pages: 16,
|
||||
/// table_size: 3,
|
||||
/// br_table_size: 3,
|
||||
/// .. Default::default()
|
||||
/// },
|
||||
/// globals: 3,
|
||||
/// parameters: 3,
|
||||
/// memory_pages: 16,
|
||||
/// table_size: 3,
|
||||
/// br_table_size: 3,
|
||||
/// .. Default::default()
|
||||
/// },
|
||||
/// instruction_weights: InstructionWeights {
|
||||
/// version: 5,
|
||||
/// version: 5,
|
||||
/// .. Default::default()
|
||||
/// },
|
||||
/// .. Default::default()
|
||||
/// .. Default::default()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
@@ -392,11 +392,13 @@ pub struct HostFnWeights<T: Config> {
|
||||
|
||||
/// The type parameter is used in the default implementation.
|
||||
#[codec(skip)]
|
||||
pub _phantom: PhantomData<T>
|
||||
pub _phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
macro_rules! replace_token {
|
||||
($_in:tt $replacement:tt) => { $replacement };
|
||||
($_in:tt $replacement:tt) => {
|
||||
$replacement
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! call_zero {
|
||||
@@ -420,20 +422,22 @@ macro_rules! cost_batched_args {
|
||||
macro_rules! cost_instr_no_params_with_batch_size {
|
||||
($name:ident, $batch_size:expr) => {
|
||||
(cost_args!($name, 1) / Weight::from($batch_size)) as u32
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_instr_with_batch_size {
|
||||
($name:ident, $num_params:expr, $batch_size:expr) => {
|
||||
cost_instr_no_params_with_batch_size!($name, $batch_size)
|
||||
.saturating_sub((cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2).saturating_mul($num_params))
|
||||
}
|
||||
cost_instr_no_params_with_batch_size!($name, $batch_size).saturating_sub(
|
||||
(cost_instr_no_params_with_batch_size!(instr_i64const, $batch_size) / 2)
|
||||
.saturating_mul($num_params),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_instr {
|
||||
($name:ident, $num_params:expr) => {
|
||||
cost_instr_with_batch_size!($name, $num_params, INSTR_BENCHMARK_BATCH_SIZE)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_byte_args {
|
||||
@@ -451,25 +455,25 @@ macro_rules! cost_byte_batched_args {
|
||||
macro_rules! cost {
|
||||
($name:ident) => {
|
||||
cost_args!($name, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_batched {
|
||||
($name:ident) => {
|
||||
cost_batched_args!($name, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_byte {
|
||||
($name:ident) => {
|
||||
cost_byte_args!($name, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cost_byte_batched {
|
||||
($name:ident) => {
|
||||
cost_byte_batched_args!($name, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Default for Limits {
|
||||
@@ -578,7 +582,11 @@ impl<T: Config> Default for HostFnWeights<T> {
|
||||
random: cost_batched!(seal_random),
|
||||
deposit_event: cost_batched!(seal_deposit_event),
|
||||
deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0),
|
||||
deposit_event_per_byte: cost_byte_batched_args!(seal_deposit_event_per_topic_and_kb, 0, 1),
|
||||
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_storage: cost_batched!(seal_set_storage),
|
||||
@@ -588,13 +596,43 @@ impl<T: Config> Default for HostFnWeights<T> {
|
||||
get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb),
|
||||
transfer: cost_batched!(seal_transfer),
|
||||
call: cost_batched!(seal_call),
|
||||
call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_input_output_kb, 1, 0, 0),
|
||||
call_per_input_byte: cost_byte_batched_args!(seal_call_per_transfer_input_output_kb, 0, 1, 0),
|
||||
call_per_output_byte: cost_byte_batched_args!(seal_call_per_transfer_input_output_kb, 0, 0, 1),
|
||||
call_transfer_surcharge: cost_batched_args!(
|
||||
seal_call_per_transfer_input_output_kb,
|
||||
1,
|
||||
0,
|
||||
0
|
||||
),
|
||||
call_per_input_byte: cost_byte_batched_args!(
|
||||
seal_call_per_transfer_input_output_kb,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
),
|
||||
call_per_output_byte: cost_byte_batched_args!(
|
||||
seal_call_per_transfer_input_output_kb,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
),
|
||||
instantiate: cost_batched!(seal_instantiate),
|
||||
instantiate_per_input_byte: cost_byte_batched_args!(seal_instantiate_per_input_output_salt_kb, 1, 0, 0),
|
||||
instantiate_per_output_byte: cost_byte_batched_args!(seal_instantiate_per_input_output_salt_kb, 0, 1, 0),
|
||||
instantiate_per_salt_byte: cost_byte_batched_args!(seal_instantiate_per_input_output_salt_kb, 0, 0, 1),
|
||||
instantiate_per_input_byte: cost_byte_batched_args!(
|
||||
seal_instantiate_per_input_output_salt_kb,
|
||||
1,
|
||||
0,
|
||||
0
|
||||
),
|
||||
instantiate_per_output_byte: cost_byte_batched_args!(
|
||||
seal_instantiate_per_input_output_salt_kb,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
),
|
||||
instantiate_per_salt_byte: cost_byte_batched_args!(
|
||||
seal_instantiate_per_input_output_salt_kb,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
),
|
||||
hash_sha2_256: cost_batched!(seal_hash_sha2_256),
|
||||
hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb),
|
||||
hash_keccak_256: cost_batched!(seal_hash_keccak_256),
|
||||
@@ -625,7 +663,7 @@ impl<T: Config> Schedule<T> {
|
||||
let elements::Type::Function(func) = func;
|
||||
func.params().len() as u32
|
||||
})
|
||||
.collect()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,12 +677,25 @@ impl<'a, T: Config> rules::Rules for ScheduleRules<'a, T> {
|
||||
let weight = match *instruction {
|
||||
End | Unreachable | Return | Else => 0,
|
||||
I32Const(_) | I64Const(_) | Block(_) | Loop(_) | Nop | Drop => w.i64const,
|
||||
I32Load(_, _) | I32Load8S(_, _) | I32Load8U(_, _) | I32Load16S(_, _) |
|
||||
I32Load16U(_, _) | I64Load(_, _) | I64Load8S(_, _) | I64Load8U(_, _) |
|
||||
I64Load16S(_, _) | I64Load16U(_, _) | I64Load32S(_, _) | I64Load32U(_, _)
|
||||
=> w.i64load,
|
||||
I32Store(_, _) | I32Store8(_, _) | I32Store16(_, _) | I64Store(_, _) |
|
||||
I64Store8(_, _) | I64Store16(_, _) | I64Store32(_, _) => w.i64store,
|
||||
I32Load(_, _) |
|
||||
I32Load8S(_, _) |
|
||||
I32Load8U(_, _) |
|
||||
I32Load16S(_, _) |
|
||||
I32Load16U(_, _) |
|
||||
I64Load(_, _) |
|
||||
I64Load8S(_, _) |
|
||||
I64Load8U(_, _) |
|
||||
I64Load16S(_, _) |
|
||||
I64Load16U(_, _) |
|
||||
I64Load32S(_, _) |
|
||||
I64Load32U(_, _) => w.i64load,
|
||||
I32Store(_, _) |
|
||||
I32Store8(_, _) |
|
||||
I32Store16(_, _) |
|
||||
I64Store(_, _) |
|
||||
I64Store8(_, _) |
|
||||
I64Store16(_, _) |
|
||||
I64Store32(_, _) => w.i64store,
|
||||
Select => w.select,
|
||||
If(_) => w.r#if,
|
||||
Br(_) => w.br,
|
||||
@@ -658,10 +709,9 @@ impl<'a, T: Config> rules::Rules for ScheduleRules<'a, T> {
|
||||
CurrentMemory(_) => w.memory_current,
|
||||
GrowMemory(_) => w.memory_grow,
|
||||
CallIndirect(idx, _) => *self.params.get(idx as usize).unwrap_or(&max_params),
|
||||
BrTable(ref data) =>
|
||||
w.br_table.saturating_add(
|
||||
w.br_table_per_entry.saturating_mul(data.table.len() as u32)
|
||||
),
|
||||
BrTable(ref data) => w
|
||||
.br_table
|
||||
.saturating_add(w.br_table_per_entry.saturating_mul(data.table.len() as u32)),
|
||||
I32Clz | I64Clz => w.i64clz,
|
||||
I32Ctz | I64Ctz => w.i64ctz,
|
||||
I32Popcnt | I64Popcnt => w.i64popcnt,
|
||||
@@ -711,8 +761,8 @@ impl<'a, T: Config> rules::Rules for ScheduleRules<'a, T> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::tests::Test;
|
||||
use super::*;
|
||||
use crate::tests::Test;
|
||||
|
||||
#[test]
|
||||
fn print_test_schedule() {
|
||||
|
||||
@@ -19,29 +19,30 @@
|
||||
|
||||
use crate::{
|
||||
exec::{AccountIdOf, StorageKey},
|
||||
BalanceOf, CodeHash, ContractInfoOf, Config, TrieId, DeletionQueue, Error,
|
||||
weights::WeightInfo,
|
||||
BalanceOf, CodeHash, Config, ContractInfoOf, DeletionQueue, Error, TrieId,
|
||||
};
|
||||
use codec::{Codec, Encode, Decode};
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::{marker::PhantomData, fmt::Debug};
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{
|
||||
RuntimeDebug,
|
||||
traits::{Bounded, Saturating, Zero, Hash, Member, MaybeSerializeDeserialize},
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use frame_support::{
|
||||
dispatch::{DispatchError, DispatchResult},
|
||||
storage::child::{self, KillStorageResult, ChildInfo},
|
||||
storage::child::{self, ChildInfo, KillStorageResult},
|
||||
traits::Get,
|
||||
weights::Weight,
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{
|
||||
traits::{Bounded, Hash, MaybeSerializeDeserialize, Member, Saturating, Zero},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
|
||||
|
||||
pub type AliveContractInfo<T> =
|
||||
RawAliveContractInfo<CodeHash<T>, BalanceOf<T>, <T as frame_system::Config>::BlockNumber>;
|
||||
pub type TombstoneContractInfo<T> =
|
||||
RawTombstoneContractInfo<<T as frame_system::Config>::Hash, <T as frame_system::Config>::Hashing>;
|
||||
pub type TombstoneContractInfo<T> = RawTombstoneContractInfo<
|
||||
<T as frame_system::Config>::Hash,
|
||||
<T as frame_system::Config>::Hashing,
|
||||
>;
|
||||
|
||||
/// Information for managing an account and its sub trie abstraction.
|
||||
/// This is the required info to cache for an account
|
||||
@@ -126,10 +127,16 @@ pub struct RawTombstoneContractInfo<H, Hasher>(H, PhantomData<Hasher>);
|
||||
|
||||
impl<H, Hasher> RawTombstoneContractInfo<H, Hasher>
|
||||
where
|
||||
H: Member + MaybeSerializeDeserialize+ Debug
|
||||
+ AsRef<[u8]> + AsMut<[u8]> + Copy + Default
|
||||
+ sp_std::hash::Hash + Codec,
|
||||
Hasher: Hash<Output=H>,
|
||||
H: Member
|
||||
+ MaybeSerializeDeserialize
|
||||
+ Debug
|
||||
+ AsRef<[u8]>
|
||||
+ AsMut<[u8]>
|
||||
+ Copy
|
||||
+ Default
|
||||
+ sp_std::hash::Hash
|
||||
+ Codec,
|
||||
Hasher: Hash<Output = H>,
|
||||
{
|
||||
pub fn new(storage_root: &[u8], code_hash: H) -> Self {
|
||||
let mut buf = Vec::new();
|
||||
@@ -156,7 +163,7 @@ pub struct Storage<T>(PhantomData<T>);
|
||||
impl<T> Storage<T>
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
/// Reads a storage kv pair of a contract.
|
||||
///
|
||||
@@ -187,11 +194,15 @@ where
|
||||
// Update the total number of KV pairs and the number of empty pairs.
|
||||
match (&opt_prev_len, &opt_new_value) {
|
||||
(Some(_), None) => {
|
||||
new_info.pair_count = new_info.pair_count.checked_sub(1)
|
||||
new_info.pair_count = new_info
|
||||
.pair_count
|
||||
.checked_sub(1)
|
||||
.ok_or_else(|| Error::<T>::StorageExhausted)?;
|
||||
},
|
||||
(None, Some(_)) => {
|
||||
new_info.pair_count = new_info.pair_count.checked_add(1)
|
||||
new_info.pair_count = new_info
|
||||
.pair_count
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| Error::<T>::StorageExhausted)?;
|
||||
},
|
||||
(Some(_), Some(_)) => {},
|
||||
@@ -200,10 +211,8 @@ where
|
||||
|
||||
// Update the total storage size.
|
||||
let prev_value_len = opt_prev_len.unwrap_or(0);
|
||||
let new_value_len = opt_new_value
|
||||
.as_ref()
|
||||
.map(|new_value| new_value.len() as u32)
|
||||
.unwrap_or(0);
|
||||
let new_value_len =
|
||||
opt_new_value.as_ref().map(|new_value| new_value.len() as u32).unwrap_or(0);
|
||||
new_info.storage_size = new_info
|
||||
.storage_size
|
||||
.checked_sub(prev_value_len)
|
||||
@@ -230,7 +239,7 @@ where
|
||||
ch: CodeHash<T>,
|
||||
) -> Result<AliveContractInfo<T>, DispatchError> {
|
||||
if <ContractInfoOf<T>>::contains_key(account) {
|
||||
return Err(Error::<T>::DuplicateContract.into());
|
||||
return Err(Error::<T>::DuplicateContract.into())
|
||||
}
|
||||
|
||||
let contract = AliveContractInfo::<T> {
|
||||
@@ -297,19 +306,17 @@ where
|
||||
pub fn process_deletion_queue_batch(weight_limit: Weight) -> Weight {
|
||||
let queue_len = <DeletionQueue<T>>::decode_len().unwrap_or(0);
|
||||
if queue_len == 0 {
|
||||
return weight_limit;
|
||||
return weight_limit
|
||||
}
|
||||
|
||||
let (weight_per_key, mut remaining_key_budget) = Self::deletion_budget(
|
||||
queue_len,
|
||||
weight_limit,
|
||||
);
|
||||
let (weight_per_key, mut remaining_key_budget) =
|
||||
Self::deletion_budget(queue_len, weight_limit);
|
||||
|
||||
// We want to check whether we have enough weight to decode the queue before
|
||||
// proceeding. Too little weight for decoding might happen during runtime upgrades
|
||||
// which consume the whole block before the other `on_initialize` blocks are called.
|
||||
if remaining_key_budget == 0 {
|
||||
return weight_limit;
|
||||
return weight_limit
|
||||
}
|
||||
|
||||
let mut queue = <DeletionQueue<T>>::get();
|
||||
@@ -318,10 +325,8 @@ where
|
||||
// Cannot panic due to loop condition
|
||||
let trie = &mut queue[0];
|
||||
let pair_count = trie.pair_count;
|
||||
let outcome = child::kill_storage(
|
||||
&child_trie_info(&trie.trie_id),
|
||||
Some(remaining_key_budget),
|
||||
);
|
||||
let outcome =
|
||||
child::kill_storage(&child_trie_info(&trie.trie_id), Some(remaining_key_budget));
|
||||
if pair_count > remaining_key_budget {
|
||||
// Cannot underflow because of the if condition
|
||||
trie.pair_count -= remaining_key_budget;
|
||||
@@ -341,8 +346,8 @@ where
|
||||
KillStorageResult::AllRemoved(_) => (),
|
||||
}
|
||||
}
|
||||
remaining_key_budget = remaining_key_budget
|
||||
.saturating_sub(remaining_key_budget.min(pair_count));
|
||||
remaining_key_budget =
|
||||
remaining_key_budget.saturating_sub(remaining_key_budget.min(pair_count));
|
||||
}
|
||||
|
||||
<DeletionQueue<T>>::put(queue);
|
||||
@@ -352,29 +357,22 @@ where
|
||||
/// This generator uses inner counter for account id and applies the hash over `AccountId +
|
||||
/// accountid_counter`.
|
||||
pub fn generate_trie_id(account_id: &AccountIdOf<T>, seed: u64) -> TrieId {
|
||||
let buf: Vec<_> = account_id.as_ref().iter()
|
||||
.chain(&seed.to_le_bytes())
|
||||
.cloned()
|
||||
.collect();
|
||||
let buf: Vec<_> = account_id.as_ref().iter().chain(&seed.to_le_bytes()).cloned().collect();
|
||||
T::Hashing::hash(&buf).as_ref().into()
|
||||
}
|
||||
|
||||
/// Returns the code hash of the contract specified by `account` ID.
|
||||
#[cfg(test)]
|
||||
pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>>
|
||||
{
|
||||
<ContractInfoOf<T>>::get(account)
|
||||
.and_then(|i| i.as_alive().map(|i| i.code_hash))
|
||||
pub fn code_hash(account: &AccountIdOf<T>) -> Option<CodeHash<T>> {
|
||||
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash))
|
||||
}
|
||||
|
||||
/// Fill up the queue in order to exercise the limits during testing.
|
||||
#[cfg(test)]
|
||||
pub fn fill_queue_with_dummies() {
|
||||
let queue: Vec<_> = (0..T::DeletionQueueDepth::get()).map(|_| DeletedContract {
|
||||
pair_count: 0,
|
||||
trie_id: vec![],
|
||||
})
|
||||
.collect();
|
||||
let queue: Vec<_> = (0..T::DeletionQueueDepth::get())
|
||||
.map(|_| DeletedContract { pair_count: 0, trie_id: vec![] })
|
||||
.collect();
|
||||
<DeletionQueue<T>>::put(queue);
|
||||
}
|
||||
}
|
||||
|
||||
+1262
-1576
File diff suppressed because it is too large
Load Diff
@@ -27,16 +27,17 @@
|
||||
//! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one.
|
||||
//! Thus, before executing a contract it should be reinstrument with new schedule.
|
||||
|
||||
use crate::{
|
||||
CodeHash, CodeStorage, PristineCode, Schedule, Config, Error, Weight,
|
||||
wasm::{prepare, PrefabWasmModule}, Pallet as Contracts, Event,
|
||||
gas::{GasMeter, Token},
|
||||
weights::WeightInfo,
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use frame_support::dispatch::DispatchError;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub use self::private::reinstrument as reinstrument;
|
||||
pub use self::private::reinstrument;
|
||||
use crate::{
|
||||
gas::{GasMeter, Token},
|
||||
wasm::{prepare, PrefabWasmModule},
|
||||
weights::WeightInfo,
|
||||
CodeHash, CodeStorage, Config, Error, Event, Pallet as Contracts, PristineCode, Schedule,
|
||||
Weight,
|
||||
};
|
||||
use frame_support::dispatch::DispatchError;
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
|
||||
/// Put the instrumented module in storage.
|
||||
///
|
||||
@@ -44,7 +45,7 @@ pub use self::private::reinstrument as reinstrument;
|
||||
/// under the specified `code_hash`.
|
||||
pub fn store<T: Config>(mut prefab_module: PrefabWasmModule<T>)
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
let code_hash = sp_std::mem::take(&mut prefab_module.code_hash);
|
||||
|
||||
@@ -53,14 +54,12 @@ where
|
||||
if let Some(code) = prefab_module.original_code.take() {
|
||||
<PristineCode<T>>::insert(&code_hash, code);
|
||||
}
|
||||
<CodeStorage<T>>::mutate(&code_hash, |existing| {
|
||||
match existing {
|
||||
Some(module) => increment_64(&mut module.refcount),
|
||||
None => {
|
||||
*existing = Some(prefab_module);
|
||||
Contracts::<T>::deposit_event(Event::CodeStored(code_hash))
|
||||
}
|
||||
}
|
||||
<CodeStorage<T>>::mutate(&code_hash, |existing| match existing {
|
||||
Some(module) => increment_64(&mut module.refcount),
|
||||
None => {
|
||||
*existing = Some(prefab_module);
|
||||
Contracts::<T>::deposit_event(Event::CodeStored(code_hash))
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -69,7 +68,7 @@ where
|
||||
/// Removes the code instead of storing it when the refcount drops to zero.
|
||||
pub fn store_decremented<T: Config>(mut prefab_module: PrefabWasmModule<T>)
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
prefab_module.refcount = prefab_module.refcount.saturating_sub(1);
|
||||
if prefab_module.refcount > 0 {
|
||||
@@ -81,10 +80,12 @@ where
|
||||
}
|
||||
|
||||
/// Increment the refcount of a code in-storage by one.
|
||||
pub fn increment_refcount<T: Config>(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>)
|
||||
-> Result<(), DispatchError>
|
||||
pub fn increment_refcount<T: Config>(
|
||||
code_hash: CodeHash<T>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
) -> Result<(), DispatchError>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
gas_meter.charge(CodeToken::UpdateRefcount(estimate_code_size::<T>(&code_hash)?))?;
|
||||
<CodeStorage<T>>::mutate(code_hash, |existing| {
|
||||
@@ -98,10 +99,12 @@ where
|
||||
}
|
||||
|
||||
/// Decrement the refcount of a code in-storage by one and remove the code when it drops to zero.
|
||||
pub fn decrement_refcount<T: Config>(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>)
|
||||
-> Result<(), DispatchError>
|
||||
pub fn decrement_refcount<T: Config>(
|
||||
code_hash: CodeHash<T>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
) -> Result<(), DispatchError>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
if let Ok(len) = estimate_code_size::<T>(&code_hash) {
|
||||
gas_meter.charge(CodeToken::UpdateRefcount(len))?;
|
||||
@@ -133,7 +136,7 @@ pub fn load<T: Config>(
|
||||
mut reinstrument: Option<(&Schedule<T>, &mut GasMeter<T>)>,
|
||||
) -> Result<PrefabWasmModule<T>, DispatchError>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
// The reinstrument case coincides with the cases where we need to charge extra
|
||||
// based upon the code size: On-chain execution.
|
||||
@@ -141,8 +144,8 @@ where
|
||||
gas_meter.charge(CodeToken::Load(estimate_code_size::<T>(&code_hash)?))?;
|
||||
}
|
||||
|
||||
let mut prefab_module = <CodeStorage<T>>::get(code_hash)
|
||||
.ok_or_else(|| Error::<T>::CodeNotFound)?;
|
||||
let mut prefab_module =
|
||||
<CodeStorage<T>>::get(code_hash).ok_or_else(|| Error::<T>::CodeNotFound)?;
|
||||
prefab_module.code_hash = code_hash;
|
||||
|
||||
if let Some((schedule, gas_meter)) = reinstrument {
|
||||
@@ -165,7 +168,7 @@ mod private {
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<(), DispatchError>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
let original_code = <PristineCode<T>>::get(&prefab_module.code_hash)
|
||||
.ok_or_else(|| Error::<T>::CodeNotFound)?;
|
||||
@@ -179,7 +182,7 @@ mod private {
|
||||
/// Finish removal of a code by deleting the pristine code and emitting an event.
|
||||
fn finish_removal<T: Config>(code_hash: CodeHash<T>)
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
<PristineCode<T>>::remove(code_hash);
|
||||
Contracts::<T>::deposit_event(Event::CodeRemoved(code_hash))
|
||||
@@ -190,13 +193,15 @@ where
|
||||
/// We try hard to be infallible here because otherwise more storage transactions would be
|
||||
/// necessary to account for failures in storing code for an already instantiated contract.
|
||||
fn increment_64(refcount: &mut u64) {
|
||||
*refcount = refcount.checked_add(1).expect("
|
||||
*refcount = refcount.checked_add(1).expect(
|
||||
"
|
||||
refcount is 64bit. Generating this overflow would require to store
|
||||
_at least_ 18 exabyte of data assuming that a contract consumes only
|
||||
one byte of data. Any node would run out of storage space before hitting
|
||||
this overflow.
|
||||
qed
|
||||
");
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
/// Get the size of the instrumented code stored at `code_hash` without loading it.
|
||||
@@ -206,7 +211,7 @@ fn increment_64(refcount: &mut u64) {
|
||||
/// compared to the code size. Additionally, charging too much weight is completely safe.
|
||||
fn estimate_code_size<T: Config>(code_hash: &CodeHash<T>) -> Result<u32, DispatchError>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
let key = <CodeStorage<T>>::hashed_key_for(code_hash);
|
||||
let mut data = [0u8; 0];
|
||||
@@ -229,7 +234,7 @@ enum CodeToken {
|
||||
impl<T> Token<T> for CodeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
fn weight(&self) -> Weight {
|
||||
use self::CodeToken::*;
|
||||
@@ -240,9 +245,10 @@ where
|
||||
// the contract.
|
||||
match *self {
|
||||
Instrument(len) => T::WeightInfo::instrument(len / 1024),
|
||||
Load(len) => T::WeightInfo::code_load(len / 1024).saturating_sub(T::WeightInfo::code_load(0)),
|
||||
UpdateRefcount(len) =>
|
||||
T::WeightInfo::code_refcount(len / 1024).saturating_sub(T::WeightInfo::code_refcount(0)),
|
||||
Load(len) =>
|
||||
T::WeightInfo::code_load(len / 1024).saturating_sub(T::WeightInfo::code_load(0)),
|
||||
UpdateRefcount(len) => T::WeightInfo::code_refcount(len / 1024)
|
||||
.saturating_sub(T::WeightInfo::code_refcount(0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,14 +255,14 @@ macro_rules! define_env {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
exec::Ext,
|
||||
wasm::{runtime::TrapReason, tests::MockExt, Runtime},
|
||||
Weight,
|
||||
};
|
||||
use pwasm_utils::parity_wasm::elements::{FunctionType, ValueType};
|
||||
use sp_runtime::traits::Zero;
|
||||
use sp_sandbox::{ReturnValue, Value};
|
||||
use crate::{
|
||||
Weight,
|
||||
wasm::{Runtime, runtime::TrapReason, tests::MockExt},
|
||||
exec::Ext,
|
||||
};
|
||||
|
||||
struct TestRuntime {
|
||||
value: u32,
|
||||
@@ -333,16 +333,15 @@ mod tests {
|
||||
Err(TrapReason::Termination)
|
||||
}
|
||||
});
|
||||
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
|
||||
-> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = seal_gas::<MockExt>;
|
||||
let _f: fn(
|
||||
&mut Runtime<MockExt>,
|
||||
&[sp_sandbox::Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = seal_gas::<MockExt>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_gen_signature() {
|
||||
assert_eq!(
|
||||
gen_signature!((i32)),
|
||||
FunctionType::new(vec![ValueType::I32], vec![]),
|
||||
);
|
||||
assert_eq!(gen_signature!((i32)), FunctionType::new(vec![ValueType::I32], vec![]),);
|
||||
|
||||
assert_eq!(
|
||||
gen_signature!( (i32, u32) -> u32 ),
|
||||
@@ -387,11 +386,11 @@ mod tests {
|
||||
},
|
||||
);
|
||||
|
||||
assert!(
|
||||
Env::can_satisfy(b"seal0", b"seal_gas",&FunctionType::new(vec![ValueType::I32], vec![]))
|
||||
);
|
||||
assert!(
|
||||
!Env::can_satisfy(b"seal0", b"not_exists", &FunctionType::new(vec![], vec![]))
|
||||
);
|
||||
assert!(Env::can_satisfy(
|
||||
b"seal0",
|
||||
b"seal_gas",
|
||||
&FunctionType::new(vec![ValueType::I32], vec![])
|
||||
));
|
||||
assert!(!Env::can_satisfy(b"seal0", b"not_exists", &FunctionType::new(vec![], vec![])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
use super::Runtime;
|
||||
use crate::exec::Ext;
|
||||
|
||||
use sp_sandbox::Value;
|
||||
use pwasm_utils::parity_wasm::elements::{FunctionType, ValueType};
|
||||
use sp_sandbox::Value;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
@@ -67,11 +67,10 @@ impl ConvertibleToWasm for u64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub type HostFunc<E> =
|
||||
fn(
|
||||
&mut Runtime<E>,
|
||||
&[sp_sandbox::Value]
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
|
||||
pub type HostFunc<E> = fn(
|
||||
&mut Runtime<E>,
|
||||
&[sp_sandbox::Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
|
||||
|
||||
pub trait FunctionImplProvider<E: Ext> {
|
||||
fn impls<F: FnMut(&[u8], &[u8], HostFunc<E>)>(f: &mut F);
|
||||
|
||||
@@ -24,19 +24,19 @@ mod code_cache;
|
||||
mod prepare;
|
||||
mod runtime;
|
||||
|
||||
use crate::{
|
||||
CodeHash, Schedule, Config,
|
||||
wasm::env_def::FunctionImplProvider,
|
||||
exec::{Ext, Executable, ExportedFunction, ExecResult},
|
||||
gas::GasMeter,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use codec::{Encode, Decode};
|
||||
use frame_support::dispatch::DispatchError;
|
||||
pub use self::runtime::{ReturnCode, Runtime, RuntimeCosts};
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub use self::code_cache::reinstrument;
|
||||
pub use self::runtime::{ReturnCode, Runtime, RuntimeCosts};
|
||||
use crate::{
|
||||
exec::{ExecResult, Executable, ExportedFunction, Ext},
|
||||
gas::GasMeter,
|
||||
wasm::env_def::FunctionImplProvider,
|
||||
CodeHash, Config, Schedule,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::dispatch::DispatchError;
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_std::prelude::*;
|
||||
#[cfg(test)]
|
||||
pub use tests::MockExt;
|
||||
|
||||
@@ -108,12 +108,12 @@ impl ExportedFunction {
|
||||
|
||||
impl<T: Config> PrefabWasmModule<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
/// Create the module by checking and instrumenting `original_code`.
|
||||
pub fn from_code(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<Self, DispatchError> {
|
||||
prepare::prepare_contract(original_code, schedule).map_err(Into::into)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ where
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn store_code_unchecked(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<(), DispatchError> {
|
||||
let executable = prepare::benchmarking::prepare_contract(original_code, schedule)
|
||||
.map_err::<DispatchError, _>(Into::into)?;
|
||||
@@ -150,7 +150,7 @@ where
|
||||
|
||||
impl<T: Config> Executable<T> for PrefabWasmModule<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
fn from_storage(
|
||||
code_hash: CodeHash<T>,
|
||||
@@ -168,15 +168,14 @@ where
|
||||
code_cache::store_decremented(self);
|
||||
}
|
||||
|
||||
fn add_user(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>)
|
||||
-> Result<(), DispatchError>
|
||||
{
|
||||
fn add_user(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>) -> Result<(), DispatchError> {
|
||||
code_cache::increment_refcount::<T>(code_hash, gas_meter)
|
||||
}
|
||||
|
||||
fn remove_user(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>)
|
||||
-> Result<(), DispatchError>
|
||||
{
|
||||
fn remove_user(
|
||||
code_hash: CodeHash<T>,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
) -> Result<(), DispatchError> {
|
||||
code_cache::decrement_refcount::<T>(code_hash, gas_meter)
|
||||
}
|
||||
|
||||
@@ -187,16 +186,15 @@ where
|
||||
input_data: Vec<u8>,
|
||||
) -> ExecResult {
|
||||
let memory =
|
||||
sp_sandbox::Memory::new(self.initial, Some(self.maximum))
|
||||
.unwrap_or_else(|_| {
|
||||
sp_sandbox::Memory::new(self.initial, Some(self.maximum)).unwrap_or_else(|_| {
|
||||
// unlike `.expect`, explicit panic preserves the source location.
|
||||
// Needed as we can't use `RUST_BACKTRACE` in here.
|
||||
panic!(
|
||||
"exec.prefab_module.initial can't be greater than exec.prefab_module.maximum;
|
||||
panic!(
|
||||
"exec.prefab_module.initial can't be greater than exec.prefab_module.maximum;
|
||||
thus Memory::new must not fail;
|
||||
qed"
|
||||
)
|
||||
});
|
||||
)
|
||||
});
|
||||
|
||||
let mut imports = sp_sandbox::EnvironmentDefinitionBuilder::new();
|
||||
imports.add_memory(self::prepare::IMPORT_MODULE_MEMORY, "memory", memory.clone());
|
||||
@@ -204,11 +202,7 @@ where
|
||||
imports.add_host_func(module, name, func_ptr);
|
||||
});
|
||||
|
||||
let mut runtime = Runtime::new(
|
||||
ext,
|
||||
input_data,
|
||||
memory,
|
||||
);
|
||||
let mut runtime = Runtime::new(ext, input_data, memory);
|
||||
|
||||
// We store before executing so that the code hash is available in the constructor.
|
||||
let code = self.code.clone();
|
||||
@@ -245,31 +239,27 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
CodeHash, BalanceOf, Error, Pallet as Contracts,
|
||||
exec::{
|
||||
Ext, StorageKey, AccountIdOf, Executable, SeedOf, BlockNumberOf,
|
||||
RentParams, ExecError, ErrorOrigin,
|
||||
AccountIdOf, BlockNumberOf, ErrorOrigin, ExecError, Executable, Ext, RentParams,
|
||||
SeedOf, StorageKey,
|
||||
},
|
||||
gas::GasMeter,
|
||||
rent::RentStatus,
|
||||
tests::{Test, Call, ALICE, BOB},
|
||||
tests::{Call, Test, ALICE, BOB},
|
||||
BalanceOf, CodeHash, Error, Pallet as Contracts,
|
||||
};
|
||||
use std::{
|
||||
borrow::BorrowMut,
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
};
|
||||
use sp_core::{Bytes, H256};
|
||||
use hex_literal::hex;
|
||||
use sp_runtime::DispatchError;
|
||||
use assert_matches::assert_matches;
|
||||
use frame_support::{
|
||||
assert_ok,
|
||||
dispatch::{DispatchResult, DispatchResultWithPostInfo},
|
||||
weights::Weight,
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use hex_literal::hex;
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sp_core::{Bytes, H256};
|
||||
use sp_runtime::DispatchError;
|
||||
use std::{borrow::BorrowMut, cell::RefCell, collections::HashMap};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct RestoreEntry {
|
||||
@@ -360,12 +350,7 @@ mod tests {
|
||||
data: Vec<u8>,
|
||||
allows_reentry: bool,
|
||||
) -> Result<ExecReturnValue, ExecError> {
|
||||
self.calls.push(CallEntry {
|
||||
to,
|
||||
value,
|
||||
data,
|
||||
allows_reentry,
|
||||
});
|
||||
self.calls.push(CallEntry { to, value, data, allows_reentry });
|
||||
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() })
|
||||
}
|
||||
fn instantiate(
|
||||
@@ -385,30 +370,15 @@ mod tests {
|
||||
});
|
||||
Ok((
|
||||
Contracts::<Test>::contract_address(&ALICE, &code_hash, salt),
|
||||
ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(Vec::new()),
|
||||
},
|
||||
ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) },
|
||||
))
|
||||
}
|
||||
fn transfer(
|
||||
&mut self,
|
||||
to: &AccountIdOf<Self::T>,
|
||||
value: u64,
|
||||
) -> Result<(), DispatchError> {
|
||||
self.transfers.push(TransferEntry {
|
||||
to: to.clone(),
|
||||
value,
|
||||
});
|
||||
fn transfer(&mut self, to: &AccountIdOf<Self::T>, value: u64) -> Result<(), DispatchError> {
|
||||
self.transfers.push(TransferEntry { to: to.clone(), value });
|
||||
Ok(())
|
||||
}
|
||||
fn terminate(
|
||||
&mut self,
|
||||
beneficiary: &AccountIdOf<Self::T>,
|
||||
) -> Result<(), DispatchError> {
|
||||
self.terminations.push(TerminationEntry {
|
||||
beneficiary: beneficiary.clone(),
|
||||
});
|
||||
fn terminate(&mut self, beneficiary: &AccountIdOf<Self::T>) -> Result<(), DispatchError> {
|
||||
self.terminations.push(TerminationEntry { beneficiary: beneficiary.clone() });
|
||||
Ok(())
|
||||
}
|
||||
fn restore_to(
|
||||
@@ -418,12 +388,7 @@ mod tests {
|
||||
rent_allowance: u64,
|
||||
delta: Vec<StorageKey>,
|
||||
) -> Result<(), DispatchError> {
|
||||
self.restores.push(RestoreEntry {
|
||||
dest,
|
||||
code_hash,
|
||||
rent_allowance,
|
||||
delta,
|
||||
});
|
||||
self.restores.push(RestoreEntry { dest, code_hash, rent_allowance, delta });
|
||||
Ok(())
|
||||
}
|
||||
fn get_storage(&mut self, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
@@ -466,8 +431,12 @@ mod tests {
|
||||
fn rent_allowance(&mut self) -> u64 {
|
||||
self.rent_allowance
|
||||
}
|
||||
fn block_number(&self) -> u64 { 121 }
|
||||
fn max_value_size(&self) -> u32 { 16_384 }
|
||||
fn block_number(&self) -> u64 {
|
||||
121
|
||||
}
|
||||
fn max_value_size(&self) -> u32 {
|
||||
16_384
|
||||
}
|
||||
fn get_weight_price(&self, weight: Weight) -> BalanceOf<Self::T> {
|
||||
BalanceOf::<Self::T>::from(1312_u32).saturating_mul(weight.into())
|
||||
}
|
||||
@@ -493,16 +462,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<E: BorrowMut<MockExt>>(
|
||||
wat: &str,
|
||||
input_data: Vec<u8>,
|
||||
mut ext: E,
|
||||
) -> ExecResult
|
||||
{
|
||||
fn execute<E: BorrowMut<MockExt>>(wat: &str, input_data: Vec<u8>, mut ext: E) -> ExecResult {
|
||||
let wasm = wat::parse_str(wat).unwrap();
|
||||
let schedule = crate::Schedule::default();
|
||||
let executable = PrefabWasmModule::<<MockExt as Ext>::T>::from_code(wasm, &schedule)
|
||||
.unwrap();
|
||||
let executable =
|
||||
PrefabWasmModule::<<MockExt as Ext>::T>::from_code(wasm, &schedule).unwrap();
|
||||
executable.execute(ext.borrow_mut(), &ExportedFunction::Call, input_data)
|
||||
}
|
||||
|
||||
@@ -543,19 +507,9 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_transfer() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(
|
||||
CODE_TRANSFER,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
));
|
||||
assert_ok!(execute(CODE_TRANSFER, vec![], &mut mock_ext,));
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.transfers,
|
||||
&[TransferEntry {
|
||||
to: ALICE,
|
||||
value: 153,
|
||||
}]
|
||||
);
|
||||
assert_eq!(&mock_ext.transfers, &[TransferEntry { to: ALICE, value: 153 }]);
|
||||
}
|
||||
|
||||
const CODE_CALL: &str = r#"
|
||||
@@ -607,20 +561,11 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_call() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(
|
||||
CODE_CALL,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
));
|
||||
assert_ok!(execute(CODE_CALL, vec![], &mut mock_ext,));
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 6,
|
||||
data: vec![1, 2, 3, 4],
|
||||
allows_reentry: true,
|
||||
}]
|
||||
&[CallEntry { to: ALICE, value: 6, data: vec![1, 2, 3, 4], allows_reentry: true }]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -675,12 +620,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: false,
|
||||
}]
|
||||
&[CallEntry { to: ALICE, value: 0x2a, data: input, allows_reentry: false }]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -736,12 +676,7 @@ mod tests {
|
||||
assert_eq!(result.data.0, input);
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: true,
|
||||
}]
|
||||
&[CallEntry { to: ALICE, value: 0x2a, data: input, allows_reentry: true }]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -789,12 +724,7 @@ mod tests {
|
||||
assert_eq!(result.data, call_return_data());
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 0x2a,
|
||||
data: input,
|
||||
allows_reentry: false,
|
||||
}]
|
||||
&[CallEntry { to: ALICE, value: 0x2a, data: input, allows_reentry: false }]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -857,11 +787,7 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_instantiate() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(
|
||||
CODE_INSTANTIATE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
));
|
||||
assert_ok!(execute(CODE_INSTANTIATE, vec![], &mut mock_ext,));
|
||||
|
||||
assert_matches!(
|
||||
&mock_ext.instantiates[..],
|
||||
@@ -905,18 +831,9 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_terminate() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
execute(
|
||||
CODE_TERMINATE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
).unwrap();
|
||||
execute(CODE_TERMINATE, vec![], &mut mock_ext).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.terminations,
|
||||
&[TerminationEntry {
|
||||
beneficiary: ALICE,
|
||||
}]
|
||||
);
|
||||
assert_eq!(&mock_ext.terminations, &[TerminationEntry { beneficiary: ALICE }]);
|
||||
}
|
||||
|
||||
const CODE_TRANSFER_LIMITED_GAS: &str = r#"
|
||||
@@ -967,20 +884,11 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_call_limited_gas() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(
|
||||
&CODE_TRANSFER_LIMITED_GAS,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
));
|
||||
assert_ok!(execute(&CODE_TRANSFER_LIMITED_GAS, vec![], &mut mock_ext,));
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.calls,
|
||||
&[CallEntry {
|
||||
to: ALICE,
|
||||
value: 6,
|
||||
data: vec![1, 2, 3, 4],
|
||||
allows_reentry: true,
|
||||
}]
|
||||
&[CallEntry { to: ALICE, value: 6, data: vec![1, 2, 3, 4], allows_reentry: true }]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1051,20 +959,14 @@ mod tests {
|
||||
#[test]
|
||||
fn get_storage_puts_data_into_buf() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
mock_ext
|
||||
.storage
|
||||
.insert([0x11; 32], [0x22; 32].to_vec());
|
||||
mock_ext.storage.insert([0x11; 32], [0x22; 32].to_vec());
|
||||
|
||||
let output = execute(
|
||||
CODE_GET_STORAGE,
|
||||
vec![],
|
||||
mock_ext,
|
||||
).unwrap();
|
||||
let output = execute(CODE_GET_STORAGE, vec![], mock_ext).unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes([0x22; 32].to_vec())
|
||||
});
|
||||
assert_eq!(
|
||||
output,
|
||||
ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes([0x22; 32].to_vec()) }
|
||||
);
|
||||
}
|
||||
|
||||
/// calls `seal_caller` and compares the result with the constant 42.
|
||||
@@ -1112,11 +1014,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn caller() {
|
||||
assert_ok!(execute(
|
||||
CODE_CALLER,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_CALLER, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
/// calls `seal_address` and compares the result with the constant 69.
|
||||
@@ -1164,11 +1062,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
assert_ok!(execute(
|
||||
CODE_ADDRESS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_ADDRESS, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_BALANCE: &str = r#"
|
||||
@@ -1214,11 +1108,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn balance() {
|
||||
assert_ok!(execute(
|
||||
CODE_BALANCE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_BALANCE, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_GAS_PRICE: &str = r#"
|
||||
@@ -1264,11 +1154,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn gas_price() {
|
||||
assert_ok!(execute(
|
||||
CODE_GAS_PRICE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_GAS_PRICE, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_GAS_LEFT: &str = r#"
|
||||
@@ -1315,11 +1201,7 @@ mod tests {
|
||||
let mut ext = MockExt::default();
|
||||
let gas_limit = ext.gas_meter.gas_left();
|
||||
|
||||
let output = execute(
|
||||
CODE_GAS_LEFT,
|
||||
vec![],
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
let output = execute(CODE_GAS_LEFT, vec![], &mut ext).unwrap();
|
||||
|
||||
let gas_left = Weight::decode(&mut &*output.data).unwrap();
|
||||
let actual_left = ext.gas_meter.gas_left();
|
||||
@@ -1370,11 +1252,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn value_transferred() {
|
||||
assert_ok!(execute(
|
||||
CODE_VALUE_TRANSFERRED,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_VALUE_TRANSFERRED, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_RETURN_FROM_START_FN: &str = r#"
|
||||
@@ -1403,18 +1281,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn return_from_start_fn() {
|
||||
let output = execute(
|
||||
CODE_RETURN_FROM_START_FN,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output = execute(CODE_RETURN_FROM_START_FN, vec![], MockExt::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
output,
|
||||
ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(vec![1, 2, 3, 4])
|
||||
}
|
||||
ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(vec![1, 2, 3, 4]) }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1461,11 +1332,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn now() {
|
||||
assert_ok!(execute(
|
||||
CODE_TIMESTAMP_NOW,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_TIMESTAMP_NOW, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_MINIMUM_BALANCE: &str = r#"
|
||||
@@ -1510,11 +1377,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn minimum_balance() {
|
||||
assert_ok!(execute(
|
||||
CODE_MINIMUM_BALANCE,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_MINIMUM_BALANCE, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_TOMBSTONE_DEPOSIT: &str = r#"
|
||||
@@ -1559,11 +1422,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tombstone_deposit() {
|
||||
assert_ok!(execute(
|
||||
CODE_TOMBSTONE_DEPOSIT,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
));
|
||||
assert_ok!(execute(CODE_TOMBSTONE_DEPOSIT, vec![], MockExt::default(),));
|
||||
}
|
||||
|
||||
const CODE_RANDOM: &str = r#"
|
||||
@@ -1622,11 +1481,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn random() {
|
||||
let output = execute(
|
||||
CODE_RANDOM,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output = execute(CODE_RANDOM, vec![], MockExt::default()).unwrap();
|
||||
|
||||
// The mock ext just returns the same data that was passed as the subject.
|
||||
assert_eq!(
|
||||
@@ -1697,26 +1552,24 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn random_v1() {
|
||||
let output = execute(
|
||||
CODE_RANDOM_V1,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output = execute(CODE_RANDOM_V1, vec![], MockExt::default()).unwrap();
|
||||
|
||||
// The mock ext just returns the same data that was passed as the subject.
|
||||
assert_eq!(
|
||||
output,
|
||||
ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes((
|
||||
data: Bytes(
|
||||
(
|
||||
hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"),
|
||||
42u64,
|
||||
).encode()),
|
||||
)
|
||||
.encode()
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const CODE_DEPOSIT_EVENT: &str = r#"
|
||||
(module
|
||||
(import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32)))
|
||||
@@ -1743,16 +1596,15 @@ mod tests {
|
||||
#[test]
|
||||
fn deposit_event() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
assert_ok!(execute(
|
||||
CODE_DEPOSIT_EVENT,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
));
|
||||
assert_ok!(execute(CODE_DEPOSIT_EVENT, vec![], &mut mock_ext,));
|
||||
|
||||
assert_eq!(mock_ext.events, vec![
|
||||
(vec![H256::repeat_byte(0x33)],
|
||||
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
|
||||
]);
|
||||
assert_eq!(
|
||||
mock_ext.events,
|
||||
vec![(
|
||||
vec![H256::repeat_byte(0x33)],
|
||||
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00]
|
||||
)]
|
||||
);
|
||||
|
||||
assert!(mock_ext.gas_meter.gas_left() > 0);
|
||||
}
|
||||
@@ -1788,11 +1640,7 @@ mod tests {
|
||||
#[test]
|
||||
fn deposit_event_max_topics() {
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_MAX_TOPICS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
),
|
||||
execute(CODE_DEPOSIT_EVENT_MAX_TOPICS, vec![], MockExt::default(),),
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::TooManyTopics.into(),
|
||||
origin: ErrorOrigin::Caller,
|
||||
@@ -1830,11 +1678,7 @@ mod tests {
|
||||
#[test]
|
||||
fn deposit_event_duplicates() {
|
||||
assert_eq!(
|
||||
execute(
|
||||
CODE_DEPOSIT_EVENT_DUPLICATES,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
),
|
||||
execute(CODE_DEPOSIT_EVENT_DUPLICATES, vec![], MockExt::default(),),
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::DuplicateTopics.into(),
|
||||
origin: ErrorOrigin::Caller,
|
||||
@@ -1887,11 +1731,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn block_number() {
|
||||
let _ = execute(
|
||||
CODE_BLOCK_NUMBER,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let _ = execute(CODE_BLOCK_NUMBER, vec![], MockExt::default()).unwrap();
|
||||
}
|
||||
|
||||
const CODE_RETURN_WITH_DATA: &str = r#"
|
||||
@@ -1932,27 +1772,32 @@ mod tests {
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("00000000445566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(hex!("445566778899").to_vec()),
|
||||
});
|
||||
assert_eq!(
|
||||
output,
|
||||
ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(hex!("445566778899").to_vec()),
|
||||
}
|
||||
);
|
||||
assert!(output.is_success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_with_revert_status() {
|
||||
let output = execute(
|
||||
CODE_RETURN_WITH_DATA,
|
||||
hex!("010000005566778899").to_vec(),
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output =
|
||||
execute(CODE_RETURN_WITH_DATA, hex!("010000005566778899").to_vec(), MockExt::default())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(output, ExecReturnValue {
|
||||
flags: ReturnFlags::REVERT,
|
||||
data: Bytes(hex!("5566778899").to_vec()),
|
||||
});
|
||||
assert_eq!(
|
||||
output,
|
||||
ExecReturnValue {
|
||||
flags: ReturnFlags::REVERT,
|
||||
data: Bytes(hex!("5566778899").to_vec()),
|
||||
}
|
||||
);
|
||||
assert!(!output.is_success());
|
||||
}
|
||||
|
||||
@@ -1975,11 +1820,7 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_out_of_bounds_access() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_OUT_OF_BOUNDS_ACCESS,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
);
|
||||
let result = execute(CODE_OUT_OF_BOUNDS_ACCESS, vec![], &mut mock_ext);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
@@ -2009,11 +1850,7 @@ mod tests {
|
||||
#[test]
|
||||
fn contract_decode_length_ignored() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_DECODE_FAILURE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
);
|
||||
let result = execute(CODE_DECODE_FAILURE, vec![], &mut mock_ext);
|
||||
// AccountID implements `MaxEncodeLen` and therefore the supplied length is
|
||||
// no longer needed nor used to determine how much is read from contract memory.
|
||||
assert_ok!(result);
|
||||
@@ -2051,17 +1888,11 @@ mod tests {
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
let output = execute(
|
||||
CODE_RENT_PARAMS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output = execute(CODE_RENT_PARAMS, vec![], MockExt::default()).unwrap();
|
||||
let rent_params = Bytes(<RentParams<Test>>::default().encode());
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_params });
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn rent_status_works() {
|
||||
@@ -2095,11 +1926,7 @@ mod tests {
|
||||
(func (export "deploy"))
|
||||
)
|
||||
"#;
|
||||
let output = execute(
|
||||
CODE_RENT_STATUS,
|
||||
vec![],
|
||||
MockExt::default(),
|
||||
).unwrap();
|
||||
let output = execute(CODE_RENT_STATUS, vec![], MockExt::default()).unwrap();
|
||||
let rent_status = Bytes(<RentStatus<Test>>::default().encode());
|
||||
assert_eq!(output, ExecReturnValue { flags: ReturnFlags::empty(), data: rent_status });
|
||||
}
|
||||
@@ -2126,11 +1953,7 @@ mod tests {
|
||||
)
|
||||
"#;
|
||||
let mut ext = MockExt::default();
|
||||
execute(
|
||||
CODE_DEBUG_MESSAGE,
|
||||
vec![],
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
execute(CODE_DEBUG_MESSAGE, vec![], &mut ext).unwrap();
|
||||
|
||||
assert_eq!(std::str::from_utf8(&ext.debug_buffer).unwrap(), "Hello World!");
|
||||
}
|
||||
@@ -2157,11 +1980,7 @@ mod tests {
|
||||
)
|
||||
"#;
|
||||
let mut ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_DEBUG_MESSAGE_FAIL,
|
||||
vec![],
|
||||
&mut ext,
|
||||
);
|
||||
let result = execute(CODE_DEBUG_MESSAGE_FAIL, vec![], &mut ext);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ExecError {
|
||||
@@ -2213,15 +2032,8 @@ mod tests {
|
||||
use std::convert::TryInto;
|
||||
let call = Call::System(frame_system::Call::remark(b"Hello World".to_vec()));
|
||||
let mut ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_CALL_RUNTIME,
|
||||
call.encode(),
|
||||
&mut ext,
|
||||
).unwrap();
|
||||
assert_eq!(
|
||||
*ext.runtime_calls.borrow(),
|
||||
vec![call],
|
||||
);
|
||||
let result = execute(CODE_CALL_RUNTIME, call.encode(), &mut ext).unwrap();
|
||||
assert_eq!(*ext.runtime_calls.borrow(), vec![call],);
|
||||
// 0 = ReturnCode::Success
|
||||
assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0);
|
||||
}
|
||||
@@ -2230,11 +2042,7 @@ mod tests {
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn call_runtime_panics_on_invalid_call() {
|
||||
let mut ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_CALL_RUNTIME,
|
||||
vec![0x42],
|
||||
&mut ext,
|
||||
);
|
||||
let result = execute(CODE_CALL_RUNTIME, vec![0x42], &mut ext);
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ExecError {
|
||||
@@ -2242,9 +2050,6 @@ mod tests {
|
||||
origin: ErrorOrigin::Caller,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
*ext.runtime_calls.borrow(),
|
||||
vec![],
|
||||
);
|
||||
assert_eq!(*ext.runtime_calls.borrow(), vec![],);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
//! from a module.
|
||||
|
||||
use crate::{
|
||||
Schedule, Config,
|
||||
chain_extension::ChainExtension,
|
||||
wasm::{PrefabWasmModule, env_def::ImportSatisfyCheck},
|
||||
wasm::{env_def::ImportSatisfyCheck, PrefabWasmModule},
|
||||
Config, Schedule,
|
||||
};
|
||||
use pwasm_utils::parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType};
|
||||
use pwasm_utils::parity_wasm::elements::{self, External, Internal, MemoryType, Type, ValueType};
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
@@ -43,10 +43,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
///
|
||||
/// Returns `Err` if the `original_code` couldn't be decoded or
|
||||
/// if it contains an invalid module.
|
||||
fn new(
|
||||
original_code: &[u8],
|
||||
schedule: &'a Schedule<T>,
|
||||
) -> Result<Self, &'static str> {
|
||||
fn new(original_code: &[u8], schedule: &'a Schedule<T>) -> Result<Self, &'static str> {
|
||||
use wasmi_validation::{validate_module, PlainValidator};
|
||||
|
||||
let module =
|
||||
@@ -57,10 +54,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
|
||||
// Return a `ContractModule` instance with
|
||||
// __valid__ module.
|
||||
Ok(ContractModule {
|
||||
module,
|
||||
schedule,
|
||||
})
|
||||
Ok(ContractModule { module, schedule })
|
||||
}
|
||||
|
||||
/// Ensures that module doesn't declare internal memories.
|
||||
@@ -69,11 +63,8 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
/// Memory section contains declarations of internal linear memories, so if we find one
|
||||
/// we reject such a module.
|
||||
fn ensure_no_internal_memory(&self) -> Result<(), &'static str> {
|
||||
if self.module
|
||||
.memory_section()
|
||||
.map_or(false, |ms| ms.entries().len() > 0)
|
||||
{
|
||||
return Err("module declares internal memory");
|
||||
if self.module.memory_section().map_or(false, |ms| ms.entries().len() > 0) {
|
||||
return Err("module declares internal memory")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -84,7 +75,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
// In Wasm MVP spec, there may be at most one table declared. Double check this
|
||||
// explicitly just in case the Wasm version changes.
|
||||
if table_section.entries().len() > 1 {
|
||||
return Err("multiple tables declared");
|
||||
return Err("multiple tables declared")
|
||||
}
|
||||
if let Some(table_type) = table_section.entries().first() {
|
||||
// Check the table's initial size as there is no instruction or environment function
|
||||
@@ -102,7 +93,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
let code_section = if let Some(type_section) = self.module.code_section() {
|
||||
type_section
|
||||
} else {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
};
|
||||
for instr in code_section.bodies().iter().flat_map(|body| body.code().elements()) {
|
||||
use self::elements::Instruction::BrTable;
|
||||
@@ -131,7 +122,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
match global.global_type().content_type() {
|
||||
ValueType::F32 | ValueType::F64 =>
|
||||
return Err("use of floating point type in globals is forbidden"),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +133,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
match local.value_type() {
|
||||
ValueType::F32 | ValueType::F64 =>
|
||||
return Err("use of floating point type in locals is forbidden"),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,11 +147,13 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
for value_type in func_type.params().iter().chain(return_type) {
|
||||
match value_type {
|
||||
ValueType::F32 | ValueType::F64 =>
|
||||
return Err("use of floating point type in function types is forbidden"),
|
||||
_ => {}
|
||||
return Err(
|
||||
"use of floating point type in function types is forbidden",
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,12 +166,12 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
let type_section = if let Some(type_section) = self.module.type_section() {
|
||||
type_section
|
||||
} else {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
for Type::Function(func) in type_section.types() {
|
||||
if func.params().len() > limit as usize {
|
||||
return Err("Use of a function type with too many parameters.");
|
||||
return Err("Use of a function type with too many parameters.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,26 +180,18 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
|
||||
fn inject_gas_metering(self) -> Result<Self, &'static str> {
|
||||
let gas_rules = self.schedule.rules(&self.module);
|
||||
let contract_module = pwasm_utils::inject_gas_counter(
|
||||
self.module,
|
||||
&gas_rules,
|
||||
"seal0",
|
||||
).map_err(|_| "gas instrumentation failed")?;
|
||||
Ok(ContractModule {
|
||||
module: contract_module,
|
||||
schedule: self.schedule,
|
||||
})
|
||||
let contract_module = pwasm_utils::inject_gas_counter(self.module, &gas_rules, "seal0")
|
||||
.map_err(|_| "gas instrumentation failed")?;
|
||||
Ok(ContractModule { module: contract_module, schedule: self.schedule })
|
||||
}
|
||||
|
||||
fn inject_stack_height_metering(self) -> Result<Self, &'static str> {
|
||||
let contract_module =
|
||||
pwasm_utils::stack_height
|
||||
::inject_limiter(self.module, self.schedule.limits.stack_height)
|
||||
.map_err(|_| "stack height instrumentation failed")?;
|
||||
Ok(ContractModule {
|
||||
module: contract_module,
|
||||
schedule: self.schedule,
|
||||
})
|
||||
let contract_module = pwasm_utils::stack_height::inject_limiter(
|
||||
self.module,
|
||||
self.schedule.limits.stack_height,
|
||||
)
|
||||
.map_err(|_| "stack height instrumentation failed")?;
|
||||
Ok(ContractModule { module: contract_module, schedule: self.schedule })
|
||||
}
|
||||
|
||||
/// Check that the module has required exported functions. For now
|
||||
@@ -223,14 +208,8 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
let module = &self.module;
|
||||
|
||||
let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]);
|
||||
let export_entries = module
|
||||
.export_section()
|
||||
.map(|is| is.entries())
|
||||
.unwrap_or(&[]);
|
||||
let func_entries = module
|
||||
.function_section()
|
||||
.map(|fs| fs.entries())
|
||||
.unwrap_or(&[]);
|
||||
let export_entries = module.export_section().map(|is| is.entries()).unwrap_or(&[]);
|
||||
let func_entries = module.function_section().map(|fs| fs.entries()).unwrap_or(&[]);
|
||||
|
||||
// Function index space consists of imported function following by
|
||||
// declared functions. Calculate the total number of imported functions so
|
||||
@@ -240,11 +219,9 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
.map(|is| is.entries())
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.filter(|entry| {
|
||||
match *entry.external() {
|
||||
External::Function(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
.filter(|entry| match *entry.external() {
|
||||
External::Function(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.count();
|
||||
|
||||
@@ -267,32 +244,32 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
Some(fn_idx) => fn_idx,
|
||||
None => {
|
||||
// Underflow here means fn_idx points to imported function which we don't allow!
|
||||
return Err("entry point points to an imported function");
|
||||
}
|
||||
return Err("entry point points to an imported function")
|
||||
},
|
||||
};
|
||||
|
||||
// Then check the signature.
|
||||
// Both "call" and "deploy" has a () -> () function type.
|
||||
// We still support () -> (i32) for backwards compatibility.
|
||||
let func_ty_idx = func_entries.get(fn_idx as usize)
|
||||
let func_ty_idx = func_entries
|
||||
.get(fn_idx as usize)
|
||||
.ok_or_else(|| "export refers to non-existent function")?
|
||||
.type_ref();
|
||||
let Type::Function(ref func_ty) = types
|
||||
.get(func_ty_idx as usize)
|
||||
.ok_or_else(|| "function has a non-existent type")?;
|
||||
if !(
|
||||
func_ty.params().is_empty() &&
|
||||
(func_ty.results().is_empty() || func_ty.results() == [ValueType::I32])
|
||||
) {
|
||||
return Err("entry point has wrong signature");
|
||||
if !(func_ty.params().is_empty() &&
|
||||
(func_ty.results().is_empty() || func_ty.results() == [ValueType::I32]))
|
||||
{
|
||||
return Err("entry point has wrong signature")
|
||||
}
|
||||
}
|
||||
|
||||
if !deploy_found {
|
||||
return Err("deploy function isn't exported");
|
||||
return Err("deploy function isn't exported")
|
||||
}
|
||||
if !call_found {
|
||||
return Err("call function isn't exported");
|
||||
return Err("call function isn't exported")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -306,16 +283,14 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
/// their signatures.
|
||||
/// - if there is a memory import, returns it's descriptor
|
||||
/// `import_fn_banlist`: list of function names that are disallowed to be imported
|
||||
fn scan_imports<C: ImportSatisfyCheck>(&self, import_fn_banlist: &[&[u8]])
|
||||
-> Result<Option<&MemoryType>, &'static str>
|
||||
{
|
||||
fn scan_imports<C: ImportSatisfyCheck>(
|
||||
&self,
|
||||
import_fn_banlist: &[&[u8]],
|
||||
) -> Result<Option<&MemoryType>, &'static str> {
|
||||
let module = &self.module;
|
||||
|
||||
let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]);
|
||||
let import_entries = module
|
||||
.import_section()
|
||||
.map(|is| is.entries())
|
||||
.unwrap_or(&[]);
|
||||
let import_entries = module.import_section().map(|is| is.entries()).unwrap_or(&[]);
|
||||
|
||||
let mut imported_mem_type = None;
|
||||
|
||||
@@ -326,7 +301,7 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
&External::Function(ref type_idx) => type_idx,
|
||||
&External::Memory(ref memory_type) => {
|
||||
if import.module() != IMPORT_MODULE_MEMORY {
|
||||
return Err("Invalid module for imported memory");
|
||||
return Err("Invalid module for imported memory")
|
||||
}
|
||||
if import.field() != "memory" {
|
||||
return Err("Memory import must have the field name 'memory'")
|
||||
@@ -335,8 +310,8 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
return Err("Multiple memory imports defined")
|
||||
}
|
||||
imported_mem_type = Some(memory_type);
|
||||
continue;
|
||||
}
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
let Type::Function(ref func_ty) = types
|
||||
@@ -346,48 +321,44 @@ impl<'a, T: Config> ContractModule<'a, T> {
|
||||
if !T::ChainExtension::enabled() &&
|
||||
import.field().as_bytes() == b"seal_call_chain_extension"
|
||||
{
|
||||
return Err("module uses chain extensions but chain extensions are disabled");
|
||||
return Err("module uses chain extensions but chain extensions are disabled")
|
||||
}
|
||||
|
||||
if import_fn_banlist.iter().any(|f| import.field().as_bytes() == *f)
|
||||
|| !C::can_satisfy(
|
||||
import.module().as_bytes(), import.field().as_bytes(), func_ty,
|
||||
)
|
||||
if import_fn_banlist.iter().any(|f| import.field().as_bytes() == *f) ||
|
||||
!C::can_satisfy(import.module().as_bytes(), import.field().as_bytes(), func_ty)
|
||||
{
|
||||
return Err("module imports a non-existent function");
|
||||
return Err("module imports a non-existent function")
|
||||
}
|
||||
}
|
||||
Ok(imported_mem_type)
|
||||
}
|
||||
|
||||
fn into_wasm_code(self) -> Result<Vec<u8>, &'static str> {
|
||||
elements::serialize(self.module)
|
||||
.map_err(|_| "error serializing instrumented module")
|
||||
elements::serialize(self.module).map_err(|_| "error serializing instrumented module")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_limits<T: Config>(module: Option<&MemoryType>, schedule: &Schedule<T>)
|
||||
-> Result<(u32, u32), &'static str>
|
||||
{
|
||||
fn get_memory_limits<T: Config>(
|
||||
module: Option<&MemoryType>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<(u32, u32), &'static str> {
|
||||
if let Some(memory_type) = module {
|
||||
// Inspect the module to extract the initial and maximum page count.
|
||||
let limits = memory_type.limits();
|
||||
match (limits.initial(), limits.maximum()) {
|
||||
(initial, Some(maximum)) if initial > maximum => {
|
||||
(initial, Some(maximum)) if initial > maximum =>
|
||||
return Err(
|
||||
"Requested initial number of pages should not exceed the requested maximum",
|
||||
);
|
||||
}
|
||||
(_, Some(maximum)) if maximum > schedule.limits.memory_pages => {
|
||||
return Err("Maximum number of pages should not exceed the configured maximum.");
|
||||
}
|
||||
),
|
||||
(_, Some(maximum)) if maximum > schedule.limits.memory_pages =>
|
||||
return Err("Maximum number of pages should not exceed the configured maximum."),
|
||||
(initial, Some(maximum)) => Ok((initial, maximum)),
|
||||
(_, None) => {
|
||||
// Maximum number of pages should be always declared.
|
||||
// This isn't a hard requirement and can be treated as a maximum set
|
||||
// to configured maximum.
|
||||
return Err("Maximum number of pages should be always declared.");
|
||||
}
|
||||
return Err("Maximum number of pages should be always declared.")
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// If none memory imported then just crate an empty placeholder.
|
||||
@@ -411,10 +382,8 @@ fn check_and_instrument<C: ImportSatisfyCheck, T: Config>(
|
||||
|
||||
// We disallow importing `gas` function here since it is treated as implementation detail.
|
||||
let disallowed_imports = [b"gas".as_ref()];
|
||||
let memory_limits = get_memory_limits(
|
||||
contract_module.scan_imports::<C>(&disallowed_imports)?,
|
||||
schedule
|
||||
)?;
|
||||
let memory_limits =
|
||||
get_memory_limits(contract_module.scan_imports::<C>(&disallowed_imports)?, schedule)?;
|
||||
|
||||
let code = contract_module
|
||||
.inject_gas_metering()?
|
||||
@@ -428,10 +397,8 @@ fn do_preparation<C: ImportSatisfyCheck, T: Config>(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<PrefabWasmModule<T>, &'static str> {
|
||||
let (code, (initial, maximum)) = check_and_instrument::<C, T>(
|
||||
original_code.as_ref(),
|
||||
schedule,
|
||||
)?;
|
||||
let (code, (initial, maximum)) =
|
||||
check_and_instrument::<C, T>(original_code.as_ref(), schedule)?;
|
||||
Ok(PrefabWasmModule {
|
||||
instruction_weights_version: schedule.instruction_weights.version,
|
||||
initial,
|
||||
@@ -483,8 +450,7 @@ pub fn reinstrument_contract<T: Config>(
|
||||
/// in production code.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub mod benchmarking {
|
||||
use super::*;
|
||||
use super::elements::FunctionType;
|
||||
use super::{elements::FunctionType, *};
|
||||
|
||||
impl ImportSatisfyCheck for () {
|
||||
fn can_satisfy(_module: &[u8], _name: &[u8], _func_type: &FunctionType) -> bool {
|
||||
@@ -493,9 +459,10 @@ pub mod benchmarking {
|
||||
}
|
||||
|
||||
/// Prepare function that neither checks nor instruments the passed in code.
|
||||
pub fn prepare_contract<T: Config>(original_code: Vec<u8>, schedule: &Schedule<T>)
|
||||
-> Result<PrefabWasmModule<T>, &'static str>
|
||||
{
|
||||
pub fn prepare_contract<T: Config>(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<PrefabWasmModule<T>, &'static str> {
|
||||
let contract_module = ContractModule::new(&original_code, schedule)?;
|
||||
let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?;
|
||||
Ok(PrefabWasmModule {
|
||||
@@ -566,7 +533,8 @@ mod tests {
|
||||
};
|
||||
}
|
||||
|
||||
prepare_test!(no_floats,
|
||||
prepare_test!(
|
||||
no_floats,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
@@ -585,7 +553,8 @@ mod tests {
|
||||
mod functions {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(param_number_valid,
|
||||
prepare_test!(
|
||||
param_number_valid,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -596,7 +565,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(param_number_invalid,
|
||||
prepare_test!(
|
||||
param_number_invalid,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -612,7 +582,8 @@ mod tests {
|
||||
mod globals {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(global_number_valid,
|
||||
prepare_test!(
|
||||
global_number_valid,
|
||||
r#"
|
||||
(module
|
||||
(global i64 (i64.const 0))
|
||||
@@ -625,7 +596,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(global_number_too_high,
|
||||
prepare_test!(
|
||||
global_number_too_high,
|
||||
r#"
|
||||
(module
|
||||
(global i64 (i64.const 0))
|
||||
@@ -643,7 +615,8 @@ mod tests {
|
||||
mod memories {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(memory_with_one_page,
|
||||
prepare_test!(
|
||||
memory_with_one_page,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1 1))
|
||||
@@ -655,7 +628,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(internal_memory_declaration,
|
||||
prepare_test!(
|
||||
internal_memory_declaration,
|
||||
r#"
|
||||
(module
|
||||
(memory 1 1)
|
||||
@@ -667,7 +641,8 @@ mod tests {
|
||||
Err("module declares internal memory")
|
||||
);
|
||||
|
||||
prepare_test!(no_memory_import,
|
||||
prepare_test!(
|
||||
no_memory_import,
|
||||
r#"
|
||||
(module
|
||||
;; no memory imported
|
||||
@@ -678,7 +653,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(initial_exceeds_maximum,
|
||||
prepare_test!(
|
||||
initial_exceeds_maximum,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 16 1))
|
||||
@@ -690,7 +666,8 @@ mod tests {
|
||||
Err("Module is not valid")
|
||||
);
|
||||
|
||||
prepare_test!(no_maximum,
|
||||
prepare_test!(
|
||||
no_maximum,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1))
|
||||
@@ -702,7 +679,8 @@ mod tests {
|
||||
Err("Maximum number of pages should be always declared.")
|
||||
);
|
||||
|
||||
prepare_test!(requested_maximum_valid,
|
||||
prepare_test!(
|
||||
requested_maximum_valid,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1 16))
|
||||
@@ -714,7 +692,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(requested_maximum_exceeds_configured_maximum,
|
||||
prepare_test!(
|
||||
requested_maximum_exceeds_configured_maximum,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1 17))
|
||||
@@ -726,7 +705,8 @@ mod tests {
|
||||
Err("Maximum number of pages should not exceed the configured maximum.")
|
||||
);
|
||||
|
||||
prepare_test!(field_name_not_memory,
|
||||
prepare_test!(
|
||||
field_name_not_memory,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "forgetit" (memory 1 1))
|
||||
@@ -738,7 +718,8 @@ mod tests {
|
||||
Err("Memory import must have the field name 'memory'")
|
||||
);
|
||||
|
||||
prepare_test!(multiple_memory_imports,
|
||||
prepare_test!(
|
||||
multiple_memory_imports,
|
||||
r#"
|
||||
(module
|
||||
(import "env" "memory" (memory 1 1))
|
||||
@@ -751,7 +732,8 @@ mod tests {
|
||||
Err("Module is not valid")
|
||||
);
|
||||
|
||||
prepare_test!(table_import,
|
||||
prepare_test!(
|
||||
table_import,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "table" (table 1 anyfunc))
|
||||
@@ -763,7 +745,8 @@ mod tests {
|
||||
Err("Cannot import tables")
|
||||
);
|
||||
|
||||
prepare_test!(global_import,
|
||||
prepare_test!(
|
||||
global_import,
|
||||
r#"
|
||||
(module
|
||||
(global $g (import "seal0" "global") i32)
|
||||
@@ -778,7 +761,8 @@ mod tests {
|
||||
mod tables {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(no_tables,
|
||||
prepare_test!(
|
||||
no_tables,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -788,7 +772,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(table_valid_size,
|
||||
prepare_test!(
|
||||
table_valid_size,
|
||||
r#"
|
||||
(module
|
||||
(table 3 funcref)
|
||||
@@ -800,7 +785,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(table_too_big,
|
||||
prepare_test!(
|
||||
table_too_big,
|
||||
r#"
|
||||
(module
|
||||
(table 4 funcref)
|
||||
@@ -811,7 +797,8 @@ mod tests {
|
||||
Err("table exceeds maximum size allowed")
|
||||
);
|
||||
|
||||
prepare_test!(br_table_valid_size,
|
||||
prepare_test!(
|
||||
br_table_valid_size,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -825,7 +812,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(br_table_too_big,
|
||||
prepare_test!(
|
||||
br_table_too_big,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -842,7 +830,8 @@ mod tests {
|
||||
mod imports {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(can_import_legit_function,
|
||||
prepare_test!(
|
||||
can_import_legit_function,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "nop" (func (param i64)))
|
||||
@@ -856,7 +845,8 @@ mod tests {
|
||||
|
||||
// even though gas is defined the contract can't import it since
|
||||
// it is an implementation defined.
|
||||
prepare_test!(can_not_import_gas_function,
|
||||
prepare_test!(
|
||||
can_not_import_gas_function,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "gas" (func (param i32)))
|
||||
@@ -869,7 +859,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// memory is in "env" and not in "seal0"
|
||||
prepare_test!(memory_not_in_seal0,
|
||||
prepare_test!(
|
||||
memory_not_in_seal0,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "memory" (memory 1 1))
|
||||
@@ -882,7 +873,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// memory is in "env" and not in some arbitrary module
|
||||
prepare_test!(memory_not_in_arbitrary_module,
|
||||
prepare_test!(
|
||||
memory_not_in_arbitrary_module,
|
||||
r#"
|
||||
(module
|
||||
(import "any_module" "memory" (memory 1 1))
|
||||
@@ -894,7 +886,8 @@ mod tests {
|
||||
Err("Invalid module for imported memory")
|
||||
);
|
||||
|
||||
prepare_test!(function_in_other_module_works,
|
||||
prepare_test!(
|
||||
function_in_other_module_works,
|
||||
r#"
|
||||
(module
|
||||
(import "seal1" "nop" (func (param i32)))
|
||||
@@ -907,7 +900,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// wrong signature
|
||||
prepare_test!(wrong_signature,
|
||||
prepare_test!(
|
||||
wrong_signature,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "gas" (func (param i64)))
|
||||
@@ -919,7 +913,8 @@ mod tests {
|
||||
Err("module imports a non-existent function")
|
||||
);
|
||||
|
||||
prepare_test!(unknown_func_name,
|
||||
prepare_test!(
|
||||
unknown_func_name,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "unknown_func" (func))
|
||||
@@ -935,7 +930,8 @@ mod tests {
|
||||
mod entrypoints {
|
||||
use super::*;
|
||||
|
||||
prepare_test!(it_works,
|
||||
prepare_test!(
|
||||
it_works,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -945,7 +941,8 @@ mod tests {
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
prepare_test!(omit_deploy,
|
||||
prepare_test!(
|
||||
omit_deploy,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -954,7 +951,8 @@ mod tests {
|
||||
Err("deploy function isn't exported")
|
||||
);
|
||||
|
||||
prepare_test!(omit_call,
|
||||
prepare_test!(
|
||||
omit_call,
|
||||
r#"
|
||||
(module
|
||||
(func (export "deploy"))
|
||||
@@ -964,7 +962,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// Try to use imported function as an entry point.
|
||||
prepare_test!(try_sneak_export_as_entrypoint,
|
||||
prepare_test!(
|
||||
try_sneak_export_as_entrypoint,
|
||||
r#"
|
||||
(module
|
||||
(import "seal0" "panic" (func))
|
||||
@@ -978,7 +977,8 @@ mod tests {
|
||||
);
|
||||
|
||||
// Try to use imported function as an entry point.
|
||||
prepare_test!(try_sneak_export_as_global,
|
||||
prepare_test!(
|
||||
try_sneak_export_as_global,
|
||||
r#"
|
||||
(module
|
||||
(func (export "deploy"))
|
||||
@@ -988,7 +988,8 @@ mod tests {
|
||||
Err("expected a function")
|
||||
);
|
||||
|
||||
prepare_test!(wrong_signature,
|
||||
prepare_test!(
|
||||
wrong_signature,
|
||||
r#"
|
||||
(module
|
||||
(func (export "deploy"))
|
||||
@@ -998,7 +999,8 @@ mod tests {
|
||||
Err("entry point has wrong signature")
|
||||
);
|
||||
|
||||
prepare_test!(unknown_exports,
|
||||
prepare_test!(
|
||||
unknown_exports,
|
||||
r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
@@ -1009,7 +1011,8 @@ mod tests {
|
||||
Err("unknown export: expecting only deploy and call functions")
|
||||
);
|
||||
|
||||
prepare_test!(global_float,
|
||||
prepare_test!(
|
||||
global_float,
|
||||
r#"
|
||||
(module
|
||||
(global $x f32 (f32.const 0))
|
||||
@@ -1020,7 +1023,8 @@ mod tests {
|
||||
Err("use of floating point type in globals is forbidden")
|
||||
);
|
||||
|
||||
prepare_test!(local_float,
|
||||
prepare_test!(
|
||||
local_float,
|
||||
r#"
|
||||
(module
|
||||
(func $foo (local f32))
|
||||
@@ -1031,7 +1035,8 @@ mod tests {
|
||||
Err("use of floating point type in locals is forbidden")
|
||||
);
|
||||
|
||||
prepare_test!(param_float,
|
||||
prepare_test!(
|
||||
param_float,
|
||||
r#"
|
||||
(module
|
||||
(func $foo (param f32))
|
||||
@@ -1042,7 +1047,8 @@ mod tests {
|
||||
Err("use of floating point type in function types is forbidden")
|
||||
);
|
||||
|
||||
prepare_test!(result_float,
|
||||
prepare_test!(
|
||||
result_float,
|
||||
r#"
|
||||
(module
|
||||
(func $foo (result f32) (f32.const 0))
|
||||
|
||||
@@ -18,25 +18,20 @@
|
||||
//! Environment definition of the wasm smart-contract runtime.
|
||||
|
||||
use crate::{
|
||||
Config, CodeHash, BalanceOf, Error,
|
||||
exec::{Ext, StorageKey, TopicOf, ExecResult, ExecError},
|
||||
gas::{Token, ChargedAmount},
|
||||
wasm::env_def::ConvertibleToWasm,
|
||||
exec::{ExecError, ExecResult, Ext, StorageKey, TopicOf},
|
||||
gas::{ChargedAmount, Token},
|
||||
schedule::HostFnWeights,
|
||||
wasm::env_def::ConvertibleToWasm,
|
||||
BalanceOf, CodeHash, Config, Error,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use pwasm_utils::parity_wasm::elements::ValueType;
|
||||
use frame_support::{dispatch::DispatchError, ensure, weights::Weight};
|
||||
use sp_std::prelude::*;
|
||||
use codec::{Decode, DecodeAll, Encode, MaxEncodedLen};
|
||||
use sp_core::{Bytes, crypto::UncheckedFrom};
|
||||
use sp_io::hashing::{
|
||||
keccak_256,
|
||||
blake2_256,
|
||||
blake2_128,
|
||||
sha2_256,
|
||||
};
|
||||
use frame_support::{dispatch::DispatchError, ensure, weights::Weight};
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
|
||||
use pwasm_utils::parity_wasm::elements::ValueType;
|
||||
use sp_core::{crypto::UncheckedFrom, Bytes};
|
||||
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Every error that can be returned to a contract when it calls any of the host functions.
|
||||
///
|
||||
@@ -178,7 +173,7 @@ pub enum RuntimeCosts {
|
||||
/// Weight of calling `seal_random`. It includes the weight for copying the subject.
|
||||
Random,
|
||||
/// 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`.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DebugMessage,
|
||||
@@ -203,7 +198,7 @@ pub enum RuntimeCosts {
|
||||
/// Weight of calling `seal_instantiate` for the given input and salt without output weight.
|
||||
/// This includes the transfer as an instantiate without a value will always be below
|
||||
/// the existential deposit and is disregarded as corner case.
|
||||
InstantiateBase{input_data_len: u32, salt_len: u32},
|
||||
InstantiateBase { input_data_len: u32, salt_len: u32 },
|
||||
/// Weight of output received through `seal_instantiate` for the given size.
|
||||
InstantiateCopyOut(u32),
|
||||
/// Weight of calling `seal_hash_sha_256` for the given input size.
|
||||
@@ -228,7 +223,7 @@ impl RuntimeCosts {
|
||||
fn token<T>(&self, s: &HostFnWeights<T>) -> RuntimeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
use self::RuntimeCosts::*;
|
||||
let weight = match *self {
|
||||
@@ -246,40 +241,44 @@ impl RuntimeCosts {
|
||||
WeightToFee => s.weight_to_fee,
|
||||
InputBase => s.input,
|
||||
InputCopyOut(len) => s.input_per_byte.saturating_mul(len.into()),
|
||||
Return(len) => s.r#return
|
||||
.saturating_add(s.return_per_byte.saturating_mul(len.into())),
|
||||
Return(len) => s.r#return.saturating_add(s.return_per_byte.saturating_mul(len.into())),
|
||||
Terminate => s.terminate,
|
||||
RestoreTo(delta) => s.restore_to
|
||||
.saturating_add(s.restore_to_per_delta.saturating_mul(delta.into())),
|
||||
RestoreTo(delta) =>
|
||||
s.restore_to.saturating_add(s.restore_to_per_delta.saturating_mul(delta.into())),
|
||||
Random => s.random,
|
||||
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_byte.saturating_mul(len.into())),
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DebugMessage => s.debug_message,
|
||||
SetRentAllowance => s.set_rent_allowance,
|
||||
SetStorage(len) => s.set_storage
|
||||
.saturating_add(s.set_storage_per_byte.saturating_mul(len.into())),
|
||||
SetStorage(len) =>
|
||||
s.set_storage.saturating_add(s.set_storage_per_byte.saturating_mul(len.into())),
|
||||
ClearStorage => s.clear_storage,
|
||||
GetStorageBase => s.get_storage,
|
||||
GetStorageCopyOut(len) => s.get_storage_per_byte.saturating_mul(len.into()),
|
||||
Transfer => s.transfer,
|
||||
CallBase(len) => s.call
|
||||
.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
|
||||
CallBase(len) =>
|
||||
s.call.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
|
||||
CallSurchargeTransfer => s.call_transfer_surcharge,
|
||||
CallCopyOut(len) => s.call_per_output_byte.saturating_mul(len.into()),
|
||||
InstantiateBase{input_data_len, salt_len} => s.instantiate
|
||||
InstantiateBase { input_data_len, salt_len } => s
|
||||
.instantiate
|
||||
.saturating_add(s.instantiate_per_input_byte.saturating_mul(input_data_len.into()))
|
||||
.saturating_add(s.instantiate_per_salt_byte.saturating_mul(salt_len.into())),
|
||||
InstantiateCopyOut(len) => s.instantiate_per_output_byte
|
||||
.saturating_mul(len.into()),
|
||||
HashSha256(len) => s.hash_sha2_256
|
||||
InstantiateCopyOut(len) => s.instantiate_per_output_byte.saturating_mul(len.into()),
|
||||
HashSha256(len) => s
|
||||
.hash_sha2_256
|
||||
.saturating_add(s.hash_sha2_256_per_byte.saturating_mul(len.into())),
|
||||
HashKeccak256(len) => s.hash_keccak_256
|
||||
HashKeccak256(len) => s
|
||||
.hash_keccak_256
|
||||
.saturating_add(s.hash_keccak_256_per_byte.saturating_mul(len.into())),
|
||||
HashBlake256(len) => s.hash_blake2_256
|
||||
HashBlake256(len) => s
|
||||
.hash_blake2_256
|
||||
.saturating_add(s.hash_blake2_256_per_byte.saturating_mul(len.into())),
|
||||
HashBlake128(len) => s.hash_blake2_128
|
||||
HashBlake128(len) => s
|
||||
.hash_blake2_128
|
||||
.saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())),
|
||||
ChainExtension(amount) => amount,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
@@ -306,7 +305,7 @@ struct RuntimeToken {
|
||||
impl<T> Token<T> for RuntimeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
fn weight(&self) -> Weight {
|
||||
self.weight
|
||||
@@ -373,19 +372,10 @@ impl<'a, E> Runtime<'a, E>
|
||||
where
|
||||
E: Ext + 'a,
|
||||
<E::T as frame_system::Config>::AccountId:
|
||||
UncheckedFrom<<E::T as frame_system::Config>::Hash> + AsRef<[u8]>
|
||||
UncheckedFrom<<E::T as frame_system::Config>::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
pub fn new(
|
||||
ext: &'a mut E,
|
||||
input_data: Vec<u8>,
|
||||
memory: sp_sandbox::Memory,
|
||||
) -> Self {
|
||||
Runtime {
|
||||
ext,
|
||||
input_data: Some(input_data),
|
||||
memory,
|
||||
trap_reason: None,
|
||||
}
|
||||
pub fn new(ext: &'a mut E, input_data: Vec<u8>, memory: sp_sandbox::Memory) -> Self {
|
||||
Runtime { ext, input_data: Some(input_data), memory, trap_reason: None }
|
||||
}
|
||||
|
||||
/// Converts the sandbox result and the runtime state into the execution outcome.
|
||||
@@ -401,27 +391,15 @@ where
|
||||
if let Some(trap_reason) = self.trap_reason {
|
||||
return match trap_reason {
|
||||
// The trap was the result of the execution `return` host function.
|
||||
TrapReason::Return(ReturnData{ flags, data }) => {
|
||||
let flags = ReturnFlags::from_bits(flags).ok_or_else(||
|
||||
"used reserved bit in return flags"
|
||||
)?;
|
||||
Ok(ExecReturnValue {
|
||||
flags,
|
||||
data: Bytes(data),
|
||||
})
|
||||
},
|
||||
TrapReason::Termination => {
|
||||
Ok(ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(Vec::new()),
|
||||
})
|
||||
},
|
||||
TrapReason::Restoration => {
|
||||
Ok(ExecReturnValue {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(Vec::new()),
|
||||
})
|
||||
TrapReason::Return(ReturnData { flags, data }) => {
|
||||
let flags = ReturnFlags::from_bits(flags)
|
||||
.ok_or_else(|| "used reserved bit in return flags")?;
|
||||
Ok(ExecReturnValue { flags, data: Bytes(data) })
|
||||
},
|
||||
TrapReason::Termination =>
|
||||
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }),
|
||||
TrapReason::Restoration =>
|
||||
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }),
|
||||
TrapReason::SupervisorError(error) => Err(error)?,
|
||||
}
|
||||
}
|
||||
@@ -429,9 +407,7 @@ where
|
||||
// Check the exact type of the error.
|
||||
match sandbox_result {
|
||||
// No traps were generated. Proceed normally.
|
||||
Ok(_) => {
|
||||
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) })
|
||||
}
|
||||
Ok(_) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Bytes(Vec::new()) }),
|
||||
// `Error::Module` is returned only if instantiation or linking failed (i.e.
|
||||
// wasm binary tried to import a function that is not provided by the host).
|
||||
// This shouldn't happen because validation process ought to reject such binaries.
|
||||
@@ -441,7 +417,7 @@ where
|
||||
Err(sp_sandbox::Error::Module) => Err("validation error")?,
|
||||
// Any other kind of a trap should result in a failure.
|
||||
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
|
||||
Err(Error::<E::T>::ContractTrapped)?
|
||||
Err(Error::<E::T>::ContractTrapped)?,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,12 +460,11 @@ where
|
||||
/// Returns `Err` if one of the following conditions occurs:
|
||||
///
|
||||
/// - requested buffer is not within the bounds of the sandbox memory.
|
||||
pub fn read_sandbox_memory(&self, ptr: u32, len: u32)
|
||||
-> Result<Vec<u8>, DispatchError>
|
||||
{
|
||||
pub fn read_sandbox_memory(&self, ptr: u32, len: u32) -> Result<Vec<u8>, DispatchError> {
|
||||
ensure!(len <= self.ext.schedule().limits.max_memory_size(), Error::<E::T>::OutOfBounds);
|
||||
let mut buf = vec![0u8; len as usize];
|
||||
self.memory.get(ptr, buf.as_mut_slice())
|
||||
self.memory
|
||||
.get(ptr, buf.as_mut_slice())
|
||||
.map_err(|_| Error::<E::T>::OutOfBounds)?;
|
||||
Ok(buf)
|
||||
}
|
||||
@@ -499,9 +474,11 @@ where
|
||||
/// Returns `Err` if one of the following conditions occurs:
|
||||
///
|
||||
/// - requested buffer is not within the bounds of the sandbox memory.
|
||||
pub fn read_sandbox_memory_into_buf(&self, ptr: u32, buf: &mut [u8])
|
||||
-> Result<(), DispatchError>
|
||||
{
|
||||
pub fn read_sandbox_memory_into_buf(
|
||||
&self,
|
||||
ptr: u32,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(), DispatchError> {
|
||||
self.memory.get(ptr, buf).map_err(|_| Error::<E::T>::OutOfBounds.into())
|
||||
}
|
||||
|
||||
@@ -511,9 +488,10 @@ where
|
||||
///
|
||||
/// The weight of reading a fixed value is included in the overall weight of any
|
||||
/// contract callable function.
|
||||
pub fn read_sandbox_memory_as<D: Decode + MaxEncodedLen>(&self, ptr: u32)
|
||||
-> Result<D, DispatchError>
|
||||
{
|
||||
pub fn read_sandbox_memory_as<D: Decode + MaxEncodedLen>(
|
||||
&self,
|
||||
ptr: u32,
|
||||
) -> Result<D, DispatchError> {
|
||||
let buf = self.read_sandbox_memory(ptr, D::max_encoded_len() as u32)?;
|
||||
let decoded = D::decode_all(&mut &buf[..])
|
||||
.map_err(|_| DispatchError::from(Error::<E::T>::DecodingFailed))?;
|
||||
@@ -531,9 +509,11 @@ where
|
||||
///
|
||||
/// There must be an extra benchmark for determining the influence of `len` with
|
||||
/// regard to the overall weight.
|
||||
pub fn read_sandbox_memory_as_unbounded<D: Decode>(&self, ptr: u32, len: u32)
|
||||
-> Result<D, DispatchError>
|
||||
{
|
||||
pub fn read_sandbox_memory_as_unbounded<D: Decode>(
|
||||
&self,
|
||||
ptr: u32,
|
||||
len: u32,
|
||||
) -> Result<D, DispatchError> {
|
||||
let buf = self.read_sandbox_memory(ptr, len)?;
|
||||
let decoded = D::decode_all(&mut &buf[..])
|
||||
.map_err(|_| DispatchError::from(Error::<E::T>::DecodingFailed))?;
|
||||
@@ -566,10 +546,9 @@ where
|
||||
buf: &[u8],
|
||||
allow_skip: bool,
|
||||
create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
|
||||
) -> Result<(), DispatchError>
|
||||
{
|
||||
) -> Result<(), DispatchError> {
|
||||
if allow_skip && out_ptr == u32::MAX {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let buf_len = buf.len() as u32;
|
||||
@@ -583,10 +562,10 @@ where
|
||||
self.charge_gas(costs)?;
|
||||
}
|
||||
|
||||
self.memory.set(out_ptr, buf).and_then(|_| {
|
||||
self.memory.set(out_len_ptr, &buf_len.encode())
|
||||
})
|
||||
.map_err(|_| Error::<E::T>::OutOfBounds)?;
|
||||
self.memory
|
||||
.set(out_ptr, buf)
|
||||
.and_then(|_| self.memory.set(out_len_ptr, &buf_len.encode()))
|
||||
.map_err(|_| Error::<E::T>::OutOfBounds)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -650,7 +629,7 @@ where
|
||||
x if x == not_funded => Ok(NewContractNotFunded),
|
||||
x if x == no_code => Ok(CodeNotFound),
|
||||
x if (x == not_found || x == is_tombstone || x == rent_not_paid) => Ok(NotCallable),
|
||||
err => Err(err)
|
||||
err => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +644,7 @@ where
|
||||
|
||||
match (error, origin) {
|
||||
(_, Callee) => Ok(ReturnCode::CalleeTrapped),
|
||||
(err, _) => Self::err_into_return_code(err)
|
||||
(err, _) => Self::err_into_return_code(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,9 +657,8 @@ where
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32
|
||||
) -> Result<ReturnCode, TrapReason>
|
||||
{
|
||||
output_len_ptr: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
self.charge_gas(RuntimeCosts::CallBase(input_data_len))?;
|
||||
let callee: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
self.read_sandbox_memory_as(callee_ptr)?;
|
||||
@@ -696,9 +674,8 @@ where
|
||||
self.charge_gas(RuntimeCosts::CallSurchargeTransfer)?;
|
||||
}
|
||||
let ext = &mut self.ext;
|
||||
let call_outcome = ext.call(
|
||||
gas, callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY),
|
||||
);
|
||||
let call_outcome =
|
||||
ext.call(gas, callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY));
|
||||
|
||||
// `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to
|
||||
// a halt anyways without anymore code being executed.
|
||||
@@ -707,7 +684,7 @@ where
|
||||
return Err(TrapReason::Return(ReturnData {
|
||||
flags: return_value.flags.bits(),
|
||||
data: return_value.data.0,
|
||||
}));
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,10 +708,9 @@ where
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32,
|
||||
salt_ptr: u32,
|
||||
salt_len: u32
|
||||
) -> Result<ReturnCode, TrapReason>
|
||||
{
|
||||
self.charge_gas(RuntimeCosts::InstantiateBase {input_data_len, salt_len})?;
|
||||
salt_len: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
self.charge_gas(RuntimeCosts::InstantiateBase { input_data_len, salt_len })?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> = self.read_sandbox_memory_as(code_hash_ptr)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = self.read_sandbox_memory_as(value_ptr)?;
|
||||
let input_data = self.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||
@@ -743,7 +719,11 @@ where
|
||||
if let Ok((address, output)) = &instantiate_outcome {
|
||||
if !output.flags.contains(ReturnFlags::REVERT) {
|
||||
self.write_sandbox_output(
|
||||
address_ptr, address_len_ptr, &address.encode(), true, already_charged,
|
||||
address_ptr,
|
||||
address_len_ptr,
|
||||
&address.encode(),
|
||||
true,
|
||||
already_charged,
|
||||
)?;
|
||||
}
|
||||
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
@@ -767,13 +747,12 @@ where
|
||||
code_hash_ptr: u32,
|
||||
rent_allowance_ptr: u32,
|
||||
delta_ptr: u32,
|
||||
delta_count: u32
|
||||
delta_count: u32,
|
||||
) -> Result<(), TrapReason> {
|
||||
self.charge_gas(RuntimeCosts::RestoreTo(delta_count))?;
|
||||
let dest: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
self.read_sandbox_memory_as(dest_ptr)?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> =
|
||||
self.read_sandbox_memory_as(code_hash_ptr)?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> = self.read_sandbox_memory_as(code_hash_ptr)?;
|
||||
let rent_allowance: BalanceOf<<E as Ext>::T> =
|
||||
self.read_sandbox_memory_as(rent_allowance_ptr)?;
|
||||
let delta = {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user