Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to 0. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 264,
impl_version: 1,
spec_version: 265,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 2,
};
Expand Down Expand Up @@ -735,6 +735,7 @@ parameter_types! {
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -
<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)
)) / 5) as u32;
pub MaxCodeSize: u32 = 128 * 1024;
}

impl pallet_contracts::Config for Runtime {
Expand All @@ -757,6 +758,7 @@ impl pallet_contracts::Config for Runtime {
type ChainExtension = ();
type DeletionQueueDepth = DeletionQueueDepth;
type DeletionWeightLimit = DeletionWeightLimit;
type MaxCodeSize = MaxCodeSize;
}

impl pallet_sudo::Config for Runtime {
Expand Down
24 changes: 20 additions & 4 deletions frame/contracts/src/benchmarking/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
use crate::Config;
use crate::Module as Contracts;

use parity_wasm::elements::{Instruction, Instructions, FuncBody, ValueType, BlockType};
use parity_wasm::elements::{
Instruction, Instructions, FuncBody, ValueType, BlockType, Section, CustomSection,
};
use pwasm_utils::stack_height::inject_limiter;
use sp_core::crypto::UncheckedFrom;
use sp_runtime::traits::Hash;
use sp_sandbox::{EnvironmentDefinitionBuilder, Memory};
use sp_std::{prelude::*, convert::TryFrom};
use sp_std::{prelude::*, convert::TryFrom, borrow::ToOwned};

/// Pass to `create_code` in order to create a compiled `WasmModule`.
///
Expand Down Expand Up @@ -66,6 +68,10 @@ pub struct ModuleDefinition {
pub inject_stack_metering: bool,
/// Create a table containing function pointers.
pub table: Option<TableSegment>,
/// Create a section named "dummy" of the specified size. This is useful in order to
/// benchmark the overhead of loading and storing codes of specified sizes. The dummy
/// section only contributes to the size of the contract but does not affect execution.
pub dummy_section: u32,
}

pub struct TableSegment {
Expand Down Expand Up @@ -204,6 +210,15 @@ where
.build();
}

// Add the dummy section
if def.dummy_section > 0 {
contract = contract.with_section(
Section::Custom(
CustomSection::new("dummy".to_owned(), vec![42; def.dummy_section as usize])
)
);
}

let mut code = contract.build();

// Inject stack height metering
Expand Down Expand Up @@ -235,10 +250,11 @@ where
ModuleDefinition::default().into()
}

/// Same as `dummy` but with maximum sized linear memory.
pub fn dummy_with_mem() -> Self {
/// Same as `dummy` but with maximum sized linear memory and a dummy section of specified size.
pub fn dummy_with_bytes(dummy_bytes: u32) -> Self {
ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
dummy_section: dummy_bytes,
.. Default::default()
}
.into()
Expand Down
92 changes: 81 additions & 11 deletions frame/contracts/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,19 @@ benchmarks! {
Storage::<T>::process_deletion_queue_batch(Weight::max_value())
}

// This benchmarks the additional weight that is charged when a contract is executed the
// first time after a new schedule was deployed: For every new schedule a contract needs
// to re-run the instrumentation once.
instrument {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c * 1024);
Contracts::<T>::store_code_raw(code)?;
let mut module = PrefabWasmModule::from_storage_noinstr(hash)?;
let schedule = Contracts::<T>::current_schedule();
}: {
Contracts::<T>::reinstrument_module(&mut module, &schedule)?;
}

// This extrinsic is pretty much constant as it is only a simple setter.
update_schedule {
let schedule = Schedule {
Expand All @@ -318,8 +331,13 @@ benchmarks! {
// determine the contract address.
// `c`: Size of the code in kilobytes.
// `s`: Size of the salt in kilobytes.
//
// # Note
//
// We cannot let `c` grow to the maximum code size because the code is not allowed
// to be larger than the maximum size **after instrumentation**.
instantiate_with_code {
let c in 0 .. Contracts::<T>::current_schedule().limits.code_size / 1024;
let c in 0 .. Perbill::from_percent(50).mul_ceil(T::MaxCodeSize::get() / 1024);
let s in 0 .. code::max_pages::<T>() * 64;
let salt = vec![42u8; (s * 1024) as usize];
let endowment = caller_funding::<T>() / 3u32.into();
Expand All @@ -339,14 +357,16 @@ benchmarks! {
}

// Instantiate uses a dummy contract constructor to measure the overhead of the instantiate.
// `c`: Size of the code in kilobytes.
// `s`: Size of the salt in kilobytes.
instantiate {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let s in 0 .. code::max_pages::<T>() * 64;
let salt = vec![42u8; (s * 1024) as usize];
let endowment = caller_funding::<T>() / 3u32.into();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy_with_mem();
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy_with_bytes(c * 1024);
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
Contracts::<T>::store_code_raw(code)?;
Expand All @@ -365,10 +385,12 @@ benchmarks! {
// won't call `seal_input` in its constructor to copy the data to contract memory.
// The dummy contract used here does not do this. The costs for the data copy is billed as
// part of `seal_input`.
// `c`: Size of the code in kilobytes.
call {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let data = vec![42u8; 1024];
let instance = Contract::<T>::with_caller(
whitelisted_caller(), WasmModule::dummy_with_mem(), vec![], Endow::CollectRent
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
)?;
let value = T::Currency::minimum_balance() * 100u32.into();
let origin = RawOrigin::Signed(instance.caller.clone());
Expand Down Expand Up @@ -396,9 +418,11 @@ benchmarks! {
// will be distributed over multiple blocks using a scheduler. Otherwise there is
// no incentive to remove large contracts when the removal is more expensive than
// the reward for removing them.
// `c`: Size of the code of the contract that should be evicted.
claim_surcharge {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let instance = Contract::<T>::with_caller(
whitelisted_caller(), WasmModule::dummy(), vec![], Endow::CollectRent
whitelisted_caller(), WasmModule::dummy_with_bytes(c * 1024), vec![], Endow::CollectRent
)?;
let origin = RawOrigin::Signed(instance.caller.clone());
let account_id = instance.account_id.clone();
Expand Down Expand Up @@ -694,6 +718,42 @@ benchmarks! {
}
}

seal_terminate_per_code_kb {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
let beneficiary_bytes = beneficiary.encode();
let beneficiary_len = beneficiary_bytes.len();
let code = WasmModule::<T>::from(ModuleDefinition {
memory: Some(ImportedMemory::max::<T>()),
imported_functions: vec![ImportedFunction {
name: "seal_terminate",
params: vec![ValueType::I32, ValueType::I32],
return_type: None,
}],
data_segments: vec![
DataSegment {
offset: 0,
value: beneficiary_bytes,
},
],
call_body: Some(body::repeated(1, &[
Instruction::I32Const(0), // beneficiary_ptr
Instruction::I32Const(beneficiary_len as i32), // beneficiary_len
Instruction::Call(0),
])),
dummy_section: c * 1024,
.. Default::default()
});
let instance = Contract::<T>::new(code, vec![], Endow::Max)?;
let origin = RawOrigin::Signed(instance.caller.clone());
assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into());
assert_eq!(T::Currency::total_balance(&instance.account_id), Endow::max::<T>());
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])
verify {
assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into());
assert_eq!(T::Currency::total_balance(&beneficiary), Endow::max::<T>());
}

seal_restore_to {
let r in 0 .. 1;

Expand Down Expand Up @@ -772,9 +832,16 @@ benchmarks! {
}
}

seal_restore_to_per_delta {
// `c`: Code size of caller contract
// `t`: Code size of tombstone contract
// `d`: Number of supplied delta keys
seal_restore_to_per_code_kb_delta {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let t in 0 .. T::MaxCodeSize::get() / 1024;
let d in 0 .. API_BENCHMARK_BATCHES;
let mut tombstone = ContractWithStorage::<T>::new(0, 0)?;
let mut tombstone = ContractWithStorage::<T>::with_code(
WasmModule::<T>::dummy_with_bytes(t * 1024), 0, 0
)?;
tombstone.evict()?;
let delta = create_storage::<T>(d * API_BENCHMARK_BATCH_SIZE, T::MaxValueSize::get())?;

Expand Down Expand Up @@ -837,6 +904,7 @@ benchmarks! {
Instruction::Call(0),
Instruction::End,
])),
dummy_section: c * 1024,
.. Default::default()
});

Expand Down Expand Up @@ -1225,7 +1293,7 @@ benchmarks! {
// We call unique accounts.
seal_call {
let r in 0 .. API_BENCHMARK_BATCHES;
let dummy_code = WasmModule::<T>::dummy_with_mem();
let dummy_code = WasmModule::<T>::dummy_with_bytes(0);
let callees = (0..r * API_BENCHMARK_BATCH_SIZE)
.map(|i| Contract::with_index(i + 1, dummy_code.clone(), vec![], Endow::Max))
.collect::<Result<Vec<_>, _>>()?;
Expand Down Expand Up @@ -1280,7 +1348,8 @@ benchmarks! {
let origin = RawOrigin::Signed(instance.caller.clone());
}: call(origin, instance.addr, 0u32.into(), Weight::max_value(), vec![])

seal_call_per_transfer_input_output_kb {
seal_call_per_code_transfer_input_output_kb {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let t in 0 .. 1;
let i in 0 .. code::max_pages::<T>() * 64;
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
Expand All @@ -1302,6 +1371,7 @@ benchmarks! {
Instruction::Call(0),
Instruction::End,
])),
dummy_section: c * 1024,
.. Default::default()
});
let callees = (0..API_BENCHMARK_BATCH_SIZE)
Expand Down Expand Up @@ -1475,7 +1545,8 @@ benchmarks! {
}
}

seal_instantiate_per_input_output_salt_kb {
seal_instantiate_per_code_input_output_salt_kb {
let c in 0 .. T::MaxCodeSize::get() / 1024;
let i in 0 .. (code::max_pages::<T>() - 1) * 64;
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
let s in 0 .. (code::max_pages::<T>() - 1) * 64;
Expand All @@ -1497,6 +1568,7 @@ benchmarks! {
Instruction::Call(0),
Instruction::End,
])),
dummy_section: c * 1024,
.. Default::default()
});
let hash = callee_code.hash.clone();
Expand Down Expand Up @@ -2440,8 +2512,6 @@ benchmarks! {
}: {}
}



impl_benchmark_test_suite!(
Contracts,
crate::tests::ExtBuilder::default().build(),
Expand Down
Loading