mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 17:41:08 +00:00
contracts: Fix seal_call weights (#10796)
* Fix call weights * Fix instantiate benchmark * cargo run --quiet --profile=production --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs * Remove stale and superflous comments * `decrement_refcount` should be infallible * Don't hardcode increment_refcount, decrement_refcount * Rename CopyIn/CopyOut * Fix warning in tests Co-authored-by: Parity Bot <admin@parity.io>
This commit is contained in:
committed by
GitHub
parent
47622d6912
commit
b82cfbac4d
@@ -33,6 +33,7 @@ use crate::{
|
||||
exec::{AccountIdOf, StorageKey},
|
||||
schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE},
|
||||
storage::Storage,
|
||||
wasm::CallFlags,
|
||||
Pallet as Contracts, *,
|
||||
};
|
||||
use codec::{Encode, MaxEncodedLen};
|
||||
@@ -1526,44 +1527,21 @@ benchmarks! {
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, callee, 0u32.into(), Weight::MAX, None, vec![])
|
||||
|
||||
seal_call_per_transfer_input_output_kb {
|
||||
seal_call_per_transfer_clone_kb {
|
||||
let t in 0 .. 1;
|
||||
let i in 0 .. code::max_pages::<T>() * 64;
|
||||
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||
let callee_code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
module: "seal0",
|
||||
name: "seal_return",
|
||||
params: vec![
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
],
|
||||
return_type: None,
|
||||
}],
|
||||
call_body: Some(body::plain(vec![
|
||||
Instruction::I32Const(0), // flags
|
||||
Instruction::I32Const(0), // data_ptr
|
||||
Instruction::I32Const((o * 1024) as i32), // data_len
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])),
|
||||
.. Default::default()
|
||||
});
|
||||
let c in 0 .. code::max_pages::<T>() * 64;
|
||||
let callees = (0..API_BENCHMARK_BATCH_SIZE)
|
||||
.map(|i| Contract::with_index(i + 1, callee_code.clone(), vec![]))
|
||||
.map(|i| Contract::with_index(i + 1, <WasmModule<T>>::dummy(), vec![]))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let callee_len = callees.get(0).map(|i| i.account_id.encode().len()).unwrap_or(0);
|
||||
let callee_bytes = callees.iter().flat_map(|x| x.account_id.encode()).collect::<Vec<_>>();
|
||||
let callees_len = callee_bytes.len();
|
||||
let value: BalanceOf<T> = t.into();
|
||||
let value_bytes = value.encode();
|
||||
let value_len = value_bytes.len();
|
||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
module: "seal0",
|
||||
module: "seal1",
|
||||
name: "seal_call",
|
||||
params: vec![
|
||||
ValueType::I32,
|
||||
@@ -1574,7 +1552,6 @@ benchmarks! {
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
],
|
||||
return_type: Some(ValueType::I32),
|
||||
}],
|
||||
@@ -1587,21 +1564,16 @@ benchmarks! {
|
||||
offset: value_len as u32,
|
||||
value: callee_bytes,
|
||||
},
|
||||
DataSegment {
|
||||
offset: (value_len + callees_len) as u32,
|
||||
value: (o * 1024).to_le_bytes().into(),
|
||||
},
|
||||
],
|
||||
call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
|
||||
Regular(Instruction::I32Const(CallFlags::CLONE_INPUT.bits() as i32)), // flags
|
||||
Counter(value_len as u32, callee_len as u32), // callee_ptr
|
||||
Regular(Instruction::I32Const(callee_len as i32)), // callee_len
|
||||
Regular(Instruction::I64Const(0)), // gas
|
||||
Regular(Instruction::I32Const(0)), // value_ptr
|
||||
Regular(Instruction::I32Const(value_len as i32)), // value_len
|
||||
Regular(Instruction::I32Const(0)), // input_data_ptr
|
||||
Regular(Instruction::I32Const((i * 1024) as i32)), // input_data_len
|
||||
Regular(Instruction::I32Const((value_len + callees_len + 4) as i32)), // output_ptr
|
||||
Regular(Instruction::I32Const((value_len + callees_len) as i32)), // output_len_ptr
|
||||
Regular(Instruction::I32Const(0)), // input_data_len
|
||||
Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr
|
||||
Regular(Instruction::I32Const(0)), // output_len_ptr
|
||||
Regular(Instruction::Call(0)),
|
||||
Regular(Instruction::Drop),
|
||||
])),
|
||||
@@ -1609,7 +1581,8 @@ benchmarks! {
|
||||
});
|
||||
let instance = Contract::<T>::new(code, vec![])?;
|
||||
let origin = RawOrigin::Signed(instance.caller.clone());
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![])
|
||||
let bytes = vec![42; (c * 1024) as usize];
|
||||
}: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, bytes)
|
||||
|
||||
// We assume that every instantiate sends at least the minimum balance.
|
||||
seal_instantiate {
|
||||
@@ -1725,52 +1698,28 @@ benchmarks! {
|
||||
}
|
||||
}
|
||||
|
||||
seal_instantiate_per_input_output_salt_kb {
|
||||
let i in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||
let o in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||
seal_instantiate_per_transfer_salt_kb {
|
||||
let t in 0 .. 1;
|
||||
let s in 0 .. (code::max_pages::<T>() - 1) * 64;
|
||||
let callee_code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
imported_functions: vec![ImportedFunction {
|
||||
module: "seal0",
|
||||
name: "seal_return",
|
||||
params: vec![
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
ValueType::I32,
|
||||
],
|
||||
return_type: None,
|
||||
}],
|
||||
deploy_body: Some(body::plain(vec![
|
||||
Instruction::I32Const(0), // flags
|
||||
Instruction::I32Const(0), // data_ptr
|
||||
Instruction::I32Const((o * 1024) as i32), // data_len
|
||||
Instruction::Call(0),
|
||||
Instruction::End,
|
||||
])),
|
||||
.. Default::default()
|
||||
});
|
||||
let callee_code = WasmModule::<T>::dummy();
|
||||
let hash = callee_code.hash.clone();
|
||||
let hash_bytes = callee_code.hash.encode();
|
||||
let hash_len = hash_bytes.len();
|
||||
Contracts::<T>::store_code_raw(callee_code.code, whitelisted_caller())?;
|
||||
let inputs = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::<Vec<_>>();
|
||||
let input_len = inputs.get(0).map(|x| x.len()).unwrap_or(0);
|
||||
let input_bytes = inputs.iter().cloned().flatten().collect::<Vec<_>>();
|
||||
let inputs_len = input_bytes.len();
|
||||
let value = T::Currency::minimum_balance();
|
||||
assert!(value > 0u32.into());
|
||||
let salts = (0..API_BENCHMARK_BATCH_SIZE).map(|x| x.encode()).collect::<Vec<_>>();
|
||||
let salt_len = salts.get(0).map(|x| x.len()).unwrap_or(0);
|
||||
let salt_bytes = salts.iter().cloned().flatten().collect::<Vec<_>>();
|
||||
let salts_len = salt_bytes.len();
|
||||
let value: BalanceOf<T> = t.into();
|
||||
let value_bytes = value.encode();
|
||||
let value_len = value_bytes.len();
|
||||
let addr_len = T::AccountId::max_encoded_len();
|
||||
|
||||
// offsets where to place static data in contract memory
|
||||
let input_offset = 0;
|
||||
let value_offset = inputs_len;
|
||||
let salt_offset = 0;
|
||||
let value_offset = salts_len;
|
||||
let hash_offset = value_offset + value_len;
|
||||
let addr_len_offset = hash_offset + hash_len;
|
||||
let output_len_offset = addr_len_offset + 4;
|
||||
let output_offset = output_len_offset + 4;
|
||||
|
||||
let code = WasmModule::<T>::from(ModuleDefinition {
|
||||
memory: Some(ImportedMemory::max::<T>()),
|
||||
@@ -1796,8 +1745,8 @@ benchmarks! {
|
||||
}],
|
||||
data_segments: vec![
|
||||
DataSegment {
|
||||
offset: input_offset as u32,
|
||||
value: input_bytes,
|
||||
offset: salt_offset as u32,
|
||||
value: salt_bytes,
|
||||
},
|
||||
DataSegment {
|
||||
offset: value_offset as u32,
|
||||
@@ -1811,10 +1760,6 @@ benchmarks! {
|
||||
offset: addr_len_offset as u32,
|
||||
value: (addr_len as u32).to_le_bytes().into(),
|
||||
},
|
||||
DataSegment {
|
||||
offset: output_len_offset as u32,
|
||||
value: (o * 1024).to_le_bytes().into(),
|
||||
},
|
||||
],
|
||||
call_body: Some(body::repeated_dyn(API_BENCHMARK_BATCH_SIZE, vec![
|
||||
Regular(Instruction::I32Const(hash_offset as i32)), // code_hash_ptr
|
||||
@@ -1822,14 +1767,14 @@ benchmarks! {
|
||||
Regular(Instruction::I64Const(0)), // gas
|
||||
Regular(Instruction::I32Const(value_offset as i32)), // value_ptr
|
||||
Regular(Instruction::I32Const(value_len as i32)), // value_len
|
||||
Counter(input_offset as u32, input_len as u32), // input_data_ptr
|
||||
Regular(Instruction::I32Const((i * 1024).max(input_len as u32) as i32)), // input_data_len
|
||||
Regular(Instruction::I32Const(0)), // input_data_ptr
|
||||
Regular(Instruction::I32Const(0)), // input_data_len
|
||||
Regular(Instruction::I32Const((addr_len_offset + addr_len) as i32)), // address_ptr
|
||||
Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr
|
||||
Regular(Instruction::I32Const(output_offset as i32)), // output_ptr
|
||||
Regular(Instruction::I32Const(output_len_offset as i32)), // output_len_ptr
|
||||
Counter(input_offset as u32, input_len as u32), // salt_ptr
|
||||
Regular(Instruction::I32Const((s * 1024).max(input_len as u32) as i32)), // salt_len
|
||||
Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr
|
||||
Regular(Instruction::I32Const(0)), // output_len_ptr
|
||||
Counter(salt_offset as u32, salt_len as u32), // salt_ptr
|
||||
Regular(Instruction::I32Const((s * 1024).max(salt_len as u32) as i32)), // salt_len
|
||||
Regular(Instruction::Call(0)),
|
||||
Regular(Instruction::I32Eqz),
|
||||
Regular(Instruction::If(BlockType::NoResult)),
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
use crate::{
|
||||
gas::GasMeter,
|
||||
storage::{self, Storage, WriteOutcome},
|
||||
wasm::{decrement_refcount, increment_refcount},
|
||||
AccountCounter, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, Error, Event,
|
||||
Pallet as Contracts, Schedule,
|
||||
};
|
||||
@@ -92,10 +91,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// Call (possibly transferring some amount of funds) into the specified account.
|
||||
///
|
||||
/// Returns the original code size of the called contract.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
|
||||
fn call(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
@@ -108,10 +103,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// Execute code in the current frame.
|
||||
///
|
||||
/// Returns the original code size of the called contract.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Result<ExecReturnValue, ExecError>
|
||||
fn delegate_call(
|
||||
&mut self,
|
||||
code: CodeHash<Self::T>,
|
||||
@@ -123,10 +114,6 @@ pub trait Ext: sealing::Sealed {
|
||||
/// Returns the original code size of the called contract.
|
||||
/// The newly created account will be associated with `code`. `value` specifies the amount of
|
||||
/// value transferred from this to the newly created account.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Result<(AccountId, ExecReturnValue, CodeSize), (ExecError, CodeSize)>
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
gas_limit: Weight,
|
||||
@@ -269,12 +256,17 @@ pub trait Executable<T: Config>: Sized {
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
) -> Result<Self, DispatchError>;
|
||||
|
||||
/// Increment the refcount of a code in-storage by one.
|
||||
///
|
||||
/// This is needed when the code is not set via instantiate but `seal_set_code_hash`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`Error::CodeNotFound`] is returned if the specified `code_hash` does not exist.
|
||||
fn add_user(code_hash: CodeHash<T>) -> Result<(), DispatchError>;
|
||||
|
||||
/// Decrement the refcount by one if the code exists.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Charges weight proportional to the code size from the gas meter.
|
||||
fn remove_user(code_hash: CodeHash<T>) -> Result<(), DispatchError>;
|
||||
fn remove_user(code_hash: CodeHash<T>);
|
||||
|
||||
/// Execute the specified exported function and return the result.
|
||||
///
|
||||
@@ -1058,7 +1050,7 @@ where
|
||||
T::Currency::free_balance(&frame.account_id),
|
||||
)?;
|
||||
ContractInfoOf::<T>::remove(&frame.account_id);
|
||||
E::remove_user(info.code_hash)?;
|
||||
E::remove_user(info.code_hash);
|
||||
Contracts::<T>::deposit_event(Event::Terminated {
|
||||
contract: frame.account_id.clone(),
|
||||
beneficiary: beneficiary.clone(),
|
||||
@@ -1188,10 +1180,10 @@ where
|
||||
}
|
||||
|
||||
fn set_code_hash(&mut self, hash: CodeHash<Self::T>) -> Result<(), DispatchError> {
|
||||
increment_refcount::<Self::T>(hash)?;
|
||||
E::add_user(hash)?;
|
||||
let top_frame = self.top_frame_mut();
|
||||
let prev_hash = top_frame.contract_info().code_hash.clone();
|
||||
decrement_refcount::<Self::T>(prev_hash.clone())?;
|
||||
E::remove_user(prev_hash.clone());
|
||||
top_frame.contract_info().code_hash = hash;
|
||||
Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated {
|
||||
contract: top_frame.account_id.clone(),
|
||||
@@ -1249,7 +1241,11 @@ mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use sp_core::Bytes;
|
||||
use sp_runtime::{traits::Hash, DispatchError};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::hash_map::{Entry, HashMap},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
type System = frame_system::Pallet<Test>;
|
||||
|
||||
@@ -1311,15 +1307,15 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn increment_refcount(code_hash: CodeHash<Test>) {
|
||||
fn increment_refcount(code_hash: CodeHash<Test>) -> Result<(), DispatchError> {
|
||||
LOADER.with(|loader| {
|
||||
let mut loader = loader.borrow_mut();
|
||||
loader
|
||||
.map
|
||||
.entry(code_hash)
|
||||
.and_modify(|executable| executable.refcount += 1)
|
||||
.or_insert_with(|| panic!("code_hash does not exist"));
|
||||
});
|
||||
match loader.map.entry(code_hash) {
|
||||
Entry::Vacant(_) => Err(<Error<Test>>::CodeNotFound)?,
|
||||
Entry::Occupied(mut entry) => entry.get_mut().refcount += 1,
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn decrement_refcount(code_hash: CodeHash<Test>) {
|
||||
@@ -1355,9 +1351,12 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_user(code_hash: CodeHash<Test>) -> Result<(), DispatchError> {
|
||||
fn add_user(code_hash: CodeHash<Test>) -> Result<(), DispatchError> {
|
||||
MockLoader::increment_refcount(code_hash)
|
||||
}
|
||||
|
||||
fn remove_user(code_hash: CodeHash<Test>) {
|
||||
MockLoader::decrement_refcount(code_hash);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute<E: Ext<T = Test>>(
|
||||
@@ -1367,7 +1366,7 @@ mod tests {
|
||||
input_data: Vec<u8>,
|
||||
) -> ExecResult {
|
||||
if let &Constructor = function {
|
||||
MockLoader::increment_refcount(self.code_hash);
|
||||
Self::add_user(self.code_hash).unwrap();
|
||||
}
|
||||
if function == &self.func_type {
|
||||
(self.func)(MockCtx { ext, input_data }, &self)
|
||||
|
||||
@@ -367,20 +367,14 @@ pub struct HostFnWeights<T: Config> {
|
||||
/// Weight surcharge that is claimed if `seal_call` does a balance transfer.
|
||||
pub call_transfer_surcharge: Weight,
|
||||
|
||||
/// Weight per input byte supplied to `seal_call`.
|
||||
pub call_per_input_byte: Weight,
|
||||
|
||||
/// Weight per output byte received through `seal_call`.
|
||||
pub call_per_output_byte: Weight,
|
||||
/// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag.
|
||||
pub call_per_cloned_byte: Weight,
|
||||
|
||||
/// Weight of calling `seal_instantiate`.
|
||||
pub instantiate: Weight,
|
||||
|
||||
/// Weight per input byte supplied to `seal_instantiate`.
|
||||
pub instantiate_per_input_byte: Weight,
|
||||
|
||||
/// Weight per output byte received through `seal_instantiate`.
|
||||
pub instantiate_per_output_byte: Weight,
|
||||
/// Weight surcharge that is claimed if `seal_instantiate` does a balance transfer.
|
||||
pub instantiate_transfer_surcharge: Weight,
|
||||
|
||||
/// Weight per salt byte supplied to `seal_instantiate`.
|
||||
pub instantiate_per_salt_byte: Weight,
|
||||
@@ -623,40 +617,16 @@ impl<T: Config> Default for HostFnWeights<T> {
|
||||
transfer: cost_batched!(seal_transfer),
|
||||
call: cost_batched!(seal_call),
|
||||
delegate_call: cost_batched!(seal_delegate_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_clone_kb, 1, 0),
|
||||
call_per_cloned_byte: cost_batched_args!(seal_call_per_transfer_clone_kb, 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,
|
||||
instantiate_transfer_surcharge: cost_byte_batched_args!(
|
||||
seal_instantiate_per_transfer_salt_kb,
|
||||
1,
|
||||
0
|
||||
),
|
||||
instantiate_per_salt_byte: cost_byte_batched_args!(
|
||||
seal_instantiate_per_input_output_salt_kb,
|
||||
0,
|
||||
seal_instantiate_per_transfer_salt_kb,
|
||||
0,
|
||||
1
|
||||
),
|
||||
|
||||
@@ -108,13 +108,12 @@ where
|
||||
///
|
||||
/// A contract whose refcount dropped to zero isn't automatically removed. A `remove_code`
|
||||
/// transaction must be submitted by the original uploader to do so.
|
||||
pub fn decrement_refcount<T: Config>(code_hash: CodeHash<T>) -> Result<(), DispatchError> {
|
||||
pub fn decrement_refcount<T: Config>(code_hash: CodeHash<T>) {
|
||||
<OwnerInfoOf<T>>::mutate(code_hash, |existing| {
|
||||
if let Some(info) = existing {
|
||||
info.refcount = info.refcount.saturating_sub(1);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Increment the refcount of a code in-storage by one.
|
||||
|
||||
@@ -25,11 +25,8 @@ mod prepare;
|
||||
mod runtime;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub use self::code_cache::reinstrument;
|
||||
pub use self::{
|
||||
code_cache::{decrement_refcount, increment_refcount},
|
||||
runtime::{ReturnCode, Runtime, RuntimeCosts},
|
||||
};
|
||||
pub use crate::wasm::code_cache::reinstrument;
|
||||
pub use crate::wasm::runtime::{CallFlags, ReturnCode, Runtime, RuntimeCosts};
|
||||
use crate::{
|
||||
exec::{ExecResult, Executable, ExportedFunction, Ext},
|
||||
gas::GasMeter,
|
||||
@@ -201,7 +198,11 @@ where
|
||||
code_cache::load(code_hash, schedule, gas_meter)
|
||||
}
|
||||
|
||||
fn remove_user(code_hash: CodeHash<T>) -> Result<(), DispatchError> {
|
||||
fn add_user(code_hash: CodeHash<T>) -> Result<(), DispatchError> {
|
||||
code_cache::increment_refcount::<T>(code_hash)
|
||||
}
|
||||
|
||||
fn remove_user(code_hash: CodeHash<T>) {
|
||||
code_cache::decrement_refcount::<T>(code_hash)
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ pub enum RuntimeCosts {
|
||||
/// Charge the gas meter with the cost of a metering block. The charged costs are
|
||||
/// the supplied cost of the block plus the overhead of the metering itself.
|
||||
MeteringBlock(u32),
|
||||
/// Weight charged for copying data from the sandbox.
|
||||
CopyFromContract(u32),
|
||||
/// Weight charged for copying data to the sandbox.
|
||||
CopyToContract(u32),
|
||||
/// Weight of calling `seal_caller`.
|
||||
Caller,
|
||||
/// Weight of calling `seal_is_contract`.
|
||||
@@ -162,8 +166,6 @@ pub enum RuntimeCosts {
|
||||
WeightToFee,
|
||||
/// Weight of calling `seal_input` without the weight of copying the input.
|
||||
InputBase,
|
||||
/// Weight of copying the input data for the given size.
|
||||
InputCopyOut(u32),
|
||||
/// Weight of calling `seal_return` for the given output size.
|
||||
Return(u32),
|
||||
/// Weight of calling `seal_terminate`.
|
||||
@@ -188,21 +190,19 @@ pub enum RuntimeCosts {
|
||||
TakeStorage(u32),
|
||||
/// Weight of calling `seal_transfer`.
|
||||
Transfer,
|
||||
/// Weight of calling `seal_call` for the given input size.
|
||||
CallBase(u32),
|
||||
/// Base weight of calling `seal_call`.
|
||||
CallBase,
|
||||
/// Weight of calling `seal_delegate_call` for the given input size.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DelegateCallBase(u32),
|
||||
DelegateCallBase,
|
||||
/// Weight of the transfer performed during a call.
|
||||
CallSurchargeTransfer,
|
||||
/// Weight of output received through `seal_call` for the given size.
|
||||
CallCopyOut(u32),
|
||||
/// 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.
|
||||
/// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag.
|
||||
CallInputCloned(u32),
|
||||
/// Weight of calling `seal_instantiate` for the given input length and salt.
|
||||
InstantiateBase { input_data_len: u32, salt_len: u32 },
|
||||
/// Weight of output received through `seal_instantiate` for the given size.
|
||||
InstantiateCopyOut(u32),
|
||||
/// Weight of the transfer performed during an instantiate.
|
||||
InstantiateSurchargeTransfer,
|
||||
/// Weight of calling `seal_hash_sha_256` for the given input size.
|
||||
HashSha256(u32),
|
||||
/// Weight of calling `seal_hash_keccak_256` for the given input size.
|
||||
@@ -216,9 +216,6 @@ pub enum RuntimeCosts {
|
||||
EcdsaRecovery,
|
||||
/// Weight charged by a chain extension through `seal_call_chain_extension`.
|
||||
ChainExtension(u64),
|
||||
/// Weight charged for copying data from the sandbox.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CopyIn(u32),
|
||||
/// Weight charged for calling into the runtime.
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CallRuntime(Weight),
|
||||
@@ -236,6 +233,8 @@ impl RuntimeCosts {
|
||||
use self::RuntimeCosts::*;
|
||||
let weight = match *self {
|
||||
MeteringBlock(amount) => s.gas.saturating_add(amount.into()),
|
||||
CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()),
|
||||
Caller => s.caller,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
IsContract => s.is_contract,
|
||||
@@ -250,7 +249,6 @@ impl RuntimeCosts {
|
||||
Now => s.now,
|
||||
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())),
|
||||
Terminate => s.terminate,
|
||||
Random => s.random,
|
||||
@@ -277,18 +275,16 @@ impl RuntimeCosts {
|
||||
.take_storage
|
||||
.saturating_add(s.take_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())),
|
||||
CallSurchargeTransfer => s.call_transfer_surcharge,
|
||||
CallCopyOut(len) => s.call_per_output_byte.saturating_mul(len.into()),
|
||||
CallBase => s.call,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
DelegateCallBase(len) =>
|
||||
s.delegate_call.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
|
||||
DelegateCallBase => s.delegate_call,
|
||||
CallSurchargeTransfer => s.call_transfer_surcharge,
|
||||
CallInputCloned(len) => s.call_per_cloned_byte.saturating_mul(len.into()),
|
||||
InstantiateBase { input_data_len, salt_len } => s
|
||||
.instantiate
|
||||
.saturating_add(s.instantiate_per_input_byte.saturating_mul(input_data_len.into()))
|
||||
.saturating_add(s.return_per_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()),
|
||||
InstantiateSurchargeTransfer => s.instantiate_transfer_surcharge,
|
||||
HashSha256(len) => s
|
||||
.hash_sha2_256
|
||||
.saturating_add(s.hash_sha2_256_per_byte.saturating_mul(len.into())),
|
||||
@@ -304,8 +300,7 @@ impl RuntimeCosts {
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
EcdsaRecovery => s.ecdsa_recover,
|
||||
ChainExtension(amount) => amount,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CallRuntime(weight) => weight,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
@@ -319,6 +314,17 @@ impl RuntimeCosts {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`Runtime::charge_gas`].
|
||||
///
|
||||
/// We need this access as a macro because sometimes hiding the lifetimes behind
|
||||
/// a function won't work out.
|
||||
macro_rules! charge_gas {
|
||||
($runtime:expr, $costs:expr) => {{
|
||||
let token = $costs.token(&$runtime.ext.schedule().host_fn_weights);
|
||||
$runtime.ext.gas_meter().charge(token)
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Copy, Clone)]
|
||||
struct RuntimeToken {
|
||||
@@ -339,7 +345,7 @@ where
|
||||
|
||||
bitflags! {
|
||||
/// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`.
|
||||
struct CallFlags: u32 {
|
||||
pub struct CallFlags: u32 {
|
||||
/// Forward the input of current function to the callee.
|
||||
///
|
||||
/// Supplied input pointers are ignored when set.
|
||||
@@ -393,11 +399,11 @@ enum CallType {
|
||||
}
|
||||
|
||||
impl CallType {
|
||||
fn cost(&self, input_data_len: u32) -> RuntimeCosts {
|
||||
fn cost(&self) -> RuntimeCosts {
|
||||
match self {
|
||||
CallType::Call { .. } => RuntimeCosts::CallBase(input_data_len),
|
||||
CallType::Call { .. } => RuntimeCosts::CallBase,
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase(input_data_len),
|
||||
CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,8 +499,7 @@ where
|
||||
///
|
||||
/// Returns `Err(HostError)` if there is not enough gas.
|
||||
pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
|
||||
let token = costs.token(&self.ext.schedule().host_fn_weights);
|
||||
self.ext.gas_meter().charge(token)
|
||||
charge_gas!(self, costs)
|
||||
}
|
||||
|
||||
/// Adjust a previously charged amount down to its actual amount.
|
||||
@@ -734,12 +739,15 @@ where
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
self.charge_gas(call_type.cost(input_data_len))?;
|
||||
self.charge_gas(call_type.cost())?;
|
||||
let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
|
||||
self.input_data.as_ref().ok_or_else(|| Error::<E::T>::InputForwarded)?.clone()
|
||||
let input = self.input_data.as_ref().ok_or_else(|| Error::<E::T>::InputForwarded)?;
|
||||
charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
|
||||
input.clone()
|
||||
} else if flags.contains(CallFlags::FORWARD_INPUT) {
|
||||
self.input_data.take().ok_or_else(|| Error::<E::T>::InputForwarded)?
|
||||
} else {
|
||||
self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
|
||||
self.read_sandbox_memory(input_data_ptr, input_data_len)?
|
||||
};
|
||||
|
||||
@@ -782,7 +790,7 @@ where
|
||||
|
||||
if let Ok(output) = &call_outcome {
|
||||
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeCosts::CallCopyOut(len))
|
||||
Some(RuntimeCosts::CopyToContract(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(call_outcome)?)
|
||||
@@ -803,8 +811,11 @@ where
|
||||
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)?;
|
||||
if value > 0u32.into() {
|
||||
self.charge_gas(RuntimeCosts::InstantiateSurchargeTransfer)?;
|
||||
}
|
||||
let code_hash: CodeHash<<E as Ext>::T> = self.read_sandbox_memory_as(code_hash_ptr)?;
|
||||
let input_data = self.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||
let salt = self.read_sandbox_memory(salt_ptr, salt_len)?;
|
||||
let instantiate_outcome = self.ext.instantiate(gas, code_hash, value, input_data, &salt);
|
||||
@@ -819,7 +830,7 @@ where
|
||||
)?;
|
||||
}
|
||||
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeCosts::InstantiateCopyOut(len))
|
||||
Some(RuntimeCosts::CopyToContract(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?)
|
||||
@@ -1302,7 +1313,7 @@ define_env!(Env, <E: Ext>,
|
||||
ctx.charge_gas(RuntimeCosts::InputBase)?;
|
||||
if let Some(input) = ctx.input_data.take() {
|
||||
ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| {
|
||||
Some(RuntimeCosts::InputCopyOut(len))
|
||||
Some(RuntimeCosts::CopyToContract(len))
|
||||
})?;
|
||||
ctx.input_data = Some(input);
|
||||
Ok(())
|
||||
@@ -1911,7 +1922,7 @@ define_env!(Env, <E: Ext>,
|
||||
// deploy a contract using it to a production chain.
|
||||
[__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => {
|
||||
use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight};
|
||||
ctx.charge_gas(RuntimeCosts::CopyIn(call_len))?;
|
||||
ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
|
||||
let call: <E::T as Config>::Call = ctx.read_sandbox_memory_as_unbounded(
|
||||
call_ptr, call_len
|
||||
)?;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user