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:
Alexander Theißen
2022-02-15 09:56:22 +01:00
committed by GitHub
parent 47622d6912
commit b82cfbac4d
7 changed files with 816 additions and 895 deletions
@@ -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)),
+31 -32
View File
@@ -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)
+9 -39
View File
@@ -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.
+7 -6
View File
@@ -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)
}
+50 -39
View File
@@ -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