mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 19:51:05 +00:00
contracts: Remove weight pre charging (#8976)
* Remove pre-charging for code size * Remove pre charging when reading values of fixed size * Add new versions of API functions that leave out parameters * Update CHANGELOG.md * Apply suggestions from code review Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Add v1 for seal_set_rent_allowance * Remove unneeded trait bound Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
This commit is contained in:
committed by
GitHub
parent
bc0520913d
commit
0cccd282a1
@@ -81,14 +81,16 @@ where
|
||||
}
|
||||
|
||||
/// Increment the refcount of a code in-storage by one.
|
||||
pub fn increment_refcount<T: Config>(code_hash: CodeHash<T>) -> Result<u32, 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]>
|
||||
{
|
||||
gas_meter.charge(CodeToken::UpdateRefcount(estimate_code_size::<T>(&code_hash)?))?;
|
||||
<CodeStorage<T>>::mutate(code_hash, |existing| {
|
||||
if let Some(module) = existing {
|
||||
increment_64(&mut module.refcount);
|
||||
Ok(module.original_code_len)
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::<T>::CodeNotFound.into())
|
||||
}
|
||||
@@ -96,23 +98,24 @@ 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>) -> u32
|
||||
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]>
|
||||
{
|
||||
if let Ok(len) = estimate_code_size::<T>(&code_hash) {
|
||||
gas_meter.charge(CodeToken::UpdateRefcount(len))?;
|
||||
}
|
||||
<CodeStorage<T>>::mutate_exists(code_hash, |existing| {
|
||||
if let Some(module) = existing {
|
||||
let code_len = module.original_code_len;
|
||||
module.refcount = module.refcount.saturating_sub(1);
|
||||
if module.refcount == 0 {
|
||||
*existing = None;
|
||||
finish_removal::<T>(code_hash);
|
||||
}
|
||||
code_len
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load code with the given code hash.
|
||||
@@ -120,13 +123,24 @@ where
|
||||
/// If the module was instrumented with a lower version of schedule than
|
||||
/// the current one given as an argument, then this function will perform
|
||||
/// re-instrumentation and update the cache in the storage.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// If `reinstrument` is set it is assumed that the load is performed in the context of
|
||||
/// a contract call: This means we charge the size based cased for loading the contract.
|
||||
pub fn load<T: Config>(
|
||||
code_hash: CodeHash<T>,
|
||||
reinstrument: Option<(&Schedule<T>, &mut GasMeter<T>)>,
|
||||
mut reinstrument: Option<(&Schedule<T>, &mut GasMeter<T>)>,
|
||||
) -> Result<PrefabWasmModule<T>, DispatchError>
|
||||
where
|
||||
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.
|
||||
if let Some((_, gas_meter)) = &mut reinstrument {
|
||||
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)?;
|
||||
prefab_module.code_hash = code_hash;
|
||||
@@ -135,7 +149,7 @@ where
|
||||
if prefab_module.instruction_weights_version < schedule.instruction_weights.version {
|
||||
// The instruction weights have changed.
|
||||
// We need to re-instrument the code with the new instruction weights.
|
||||
gas_meter.charge(InstrumentToken(prefab_module.original_code_len))?;
|
||||
gas_meter.charge(CodeToken::Instrument(prefab_module.original_code_len))?;
|
||||
private::reinstrument(&mut prefab_module, schedule)?;
|
||||
}
|
||||
}
|
||||
@@ -185,14 +199,50 @@ fn increment_64(refcount: &mut u64) {
|
||||
");
|
||||
}
|
||||
|
||||
/// Token to be supplied to the gas meter which charges the weight needed for reinstrumenting
|
||||
/// a contract of the specified size in bytes.
|
||||
/// Get the size of the instrumented code stored at `code_hash` without loading it.
|
||||
///
|
||||
/// The returned value is slightly too large because it also contains the fields apart from
|
||||
/// `code` which are located inside [`PrefabWasmModule`]. However, those are negligible when
|
||||
/// 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]>
|
||||
{
|
||||
let key = <CodeStorage<T>>::hashed_key_for(code_hash);
|
||||
let mut data = [0u8; 0];
|
||||
let len = sp_io::storage::read(&key, &mut data, 0).ok_or_else(|| Error::<T>::CodeNotFound)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
/// Costs for operations that are related to code handling.
|
||||
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone, Copy)]
|
||||
struct InstrumentToken(u32);
|
||||
enum CodeToken {
|
||||
/// Weight for instrumenting a contract contract of the supplied size in bytes.
|
||||
Instrument(u32),
|
||||
/// Weight for loading a contract per kilobyte.
|
||||
Load(u32),
|
||||
/// Weight for changing the refcount of a contract per kilobyte.
|
||||
UpdateRefcount(u32),
|
||||
}
|
||||
|
||||
impl<T: Config> Token<T> for InstrumentToken {
|
||||
impl<T> Token<T> for CodeToken
|
||||
where
|
||||
T: Config,
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
{
|
||||
fn weight(&self) -> Weight {
|
||||
T::WeightInfo::instrument(self.0 / 1024)
|
||||
use self::CodeToken::*;
|
||||
// In case of `Load` and `UpdateRefcount` we already covered the general costs of
|
||||
// accessing the storage but still need to account for the actual size of the
|
||||
// contract code. This is why we substract `T::*::(0)`. We need to do this at this
|
||||
// point because when charging the general weight we do not know the size of
|
||||
// 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,12 +168,16 @@ where
|
||||
code_cache::store_decremented(self);
|
||||
}
|
||||
|
||||
fn add_user(code_hash: CodeHash<T>) -> Result<u32, DispatchError> {
|
||||
code_cache::increment_refcount::<T>(code_hash)
|
||||
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>) -> u32 {
|
||||
code_cache::decrement_refcount::<T>(code_hash)
|
||||
fn remove_user(code_hash: CodeHash<T>, gas_meter: &mut GasMeter<T>)
|
||||
-> Result<(), DispatchError>
|
||||
{
|
||||
code_cache::decrement_refcount::<T>(code_hash, gas_meter)
|
||||
}
|
||||
|
||||
fn execute<E: Ext<T = T>>(
|
||||
@@ -349,14 +353,14 @@ mod tests {
|
||||
value: u64,
|
||||
data: Vec<u8>,
|
||||
allows_reentry: bool,
|
||||
) -> Result<(ExecReturnValue, u32), (ExecError, u32)> {
|
||||
) -> Result<ExecReturnValue, ExecError> {
|
||||
self.calls.push(CallEntry {
|
||||
to,
|
||||
value,
|
||||
data,
|
||||
allows_reentry,
|
||||
});
|
||||
Ok((ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() }, 0))
|
||||
Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: call_return_data() })
|
||||
}
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
@@ -365,7 +369,7 @@ mod tests {
|
||||
endowment: u64,
|
||||
data: Vec<u8>,
|
||||
salt: &[u8],
|
||||
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue, u32), (ExecError, u32)> {
|
||||
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue), ExecError> {
|
||||
self.instantiates.push(InstantiateEntry {
|
||||
code_hash: code_hash.clone(),
|
||||
endowment,
|
||||
@@ -379,7 +383,6 @@ mod tests {
|
||||
flags: ReturnFlags::empty(),
|
||||
data: Bytes(Vec::new()),
|
||||
},
|
||||
0,
|
||||
))
|
||||
}
|
||||
fn transfer(
|
||||
@@ -396,11 +399,11 @@ mod tests {
|
||||
fn terminate(
|
||||
&mut self,
|
||||
beneficiary: &AccountIdOf<Self::T>,
|
||||
) -> Result<u32, (DispatchError, u32)> {
|
||||
) -> Result<(), DispatchError> {
|
||||
self.terminations.push(TerminationEntry {
|
||||
beneficiary: beneficiary.clone(),
|
||||
});
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
@@ -408,14 +411,14 @@ mod tests {
|
||||
code_hash: H256,
|
||||
rent_allowance: u64,
|
||||
delta: Vec<StorageKey>,
|
||||
) -> Result<(u32, u32), (DispatchError, u32, u32)> {
|
||||
) -> Result<(), DispatchError> {
|
||||
self.restores.push(RestoreEntry {
|
||||
dest,
|
||||
code_hash,
|
||||
rent_allowance,
|
||||
delta,
|
||||
});
|
||||
Ok((0, 0))
|
||||
Ok(())
|
||||
}
|
||||
fn get_storage(&mut self, key: &StorageKey) -> Option<Vec<u8>> {
|
||||
self.storage.get(key).cloned()
|
||||
@@ -616,7 +619,7 @@ mod tests {
|
||||
fn contract_call_forward_input() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
@@ -624,10 +627,8 @@ mod tests {
|
||||
(call $seal_call
|
||||
(i32.const 1) ;; Set FORWARD_INPUT bit
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 44) ;; Pointer to input data buffer address
|
||||
(i32.const 4) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
@@ -678,7 +679,7 @@ mod tests {
|
||||
fn contract_call_clone_input() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
@@ -687,10 +688,8 @@ mod tests {
|
||||
(call $seal_call
|
||||
(i32.const 11) ;; Set FORWARD_INPUT | CLONE_INPUT | ALLOW_REENTRY bits
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 44) ;; Pointer to input data buffer address
|
||||
(i32.const 4) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
@@ -741,17 +740,15 @@ mod tests {
|
||||
fn contract_call_tail_call() {
|
||||
const CODE: &str = r#"
|
||||
(module
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "__unstable__" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "call")
|
||||
(drop
|
||||
(call $seal_call
|
||||
(i32.const 5) ;; Set FORWARD_INPUT | TAIL_CALL bit
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 8) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 0) ;; Pointer to input data buffer address
|
||||
(i32.const 0) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
@@ -2000,25 +1997,18 @@ mod tests {
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn contract_decode_failure() {
|
||||
fn contract_decode_length_ignored() {
|
||||
let mut mock_ext = MockExt::default();
|
||||
let result = execute(
|
||||
CODE_DECODE_FAILURE,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(ExecError {
|
||||
error: Error::<Test>::DecodingFailed.into(),
|
||||
origin: ErrorOrigin::Caller,
|
||||
})
|
||||
);
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unstable-interface")]
|
||||
fn rent_params_work() {
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::{
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use pwasm_utils::parity_wasm::elements::ValueType;
|
||||
use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight};
|
||||
use frame_support::{dispatch::DispatchError, ensure, weights::Weight, traits::MaxEncodedLen};
|
||||
use sp_std::prelude::*;
|
||||
use codec::{Decode, DecodeAll, Encode};
|
||||
use sp_core::{Bytes, crypto::UncheckedFrom};
|
||||
@@ -170,12 +170,8 @@ pub enum RuntimeCosts {
|
||||
Return(u32),
|
||||
/// Weight of calling `seal_terminate`.
|
||||
Terminate,
|
||||
/// Weight that is added to `seal_terminate` for every byte of the terminated contract.
|
||||
TerminateSurchargeCodeSize(u32),
|
||||
/// Weight of calling `seal_restore_to` per number of supplied delta entries.
|
||||
RestoreTo(u32),
|
||||
/// Weight that is added to `seal_restore_to` for the involved code sizes.
|
||||
RestoreToSurchargeCodeSize{caller_code: u32, tombstone_code: u32},
|
||||
/// 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.
|
||||
@@ -197,8 +193,6 @@ pub enum RuntimeCosts {
|
||||
Transfer,
|
||||
/// Weight of calling `seal_call` for the given input size.
|
||||
CallBase(u32),
|
||||
/// Weight that is added to `seal_call` for every byte of the called contract.
|
||||
CallSurchargeCodeSize(u32),
|
||||
/// Weight of the transfer performed during a call.
|
||||
CallSurchargeTransfer,
|
||||
/// Weight of output received through `seal_call` for the given size.
|
||||
@@ -207,8 +201,6 @@ pub enum RuntimeCosts {
|
||||
/// 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},
|
||||
/// Weight that is added to `seal_instantiate` for every byte of the instantiated contract.
|
||||
InstantiateSurchargeCodeSize(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.
|
||||
@@ -221,8 +213,6 @@ pub enum RuntimeCosts {
|
||||
HashBlake128(u32),
|
||||
/// Weight charged by a chain extension through `seal_call_chain_extension`.
|
||||
ChainExtension(u64),
|
||||
/// Weight charged for copying data from the sandbox.
|
||||
CopyIn(u32),
|
||||
}
|
||||
|
||||
impl RuntimeCosts {
|
||||
@@ -250,13 +240,8 @@ impl RuntimeCosts {
|
||||
Return(len) => s.r#return
|
||||
.saturating_add(s.return_per_byte.saturating_mul(len.into())),
|
||||
Terminate => s.terminate,
|
||||
TerminateSurchargeCodeSize(len) => s.terminate_per_code_byte.saturating_mul(len.into()),
|
||||
RestoreTo(delta) => s.restore_to
|
||||
.saturating_add(s.restore_to_per_delta.saturating_mul(delta.into())),
|
||||
RestoreToSurchargeCodeSize{caller_code, tombstone_code} =>
|
||||
s.restore_to_per_caller_code_byte.saturating_mul(caller_code.into()).saturating_add(
|
||||
s.restore_to_per_tombstone_code_byte.saturating_mul(tombstone_code.into())
|
||||
),
|
||||
Random => s.random,
|
||||
DepositEvent{num_topic, len} => s.deposit_event
|
||||
.saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into()))
|
||||
@@ -272,14 +257,11 @@ impl RuntimeCosts {
|
||||
Transfer => s.transfer,
|
||||
CallBase(len) => s.call
|
||||
.saturating_add(s.call_per_input_byte.saturating_mul(len.into())),
|
||||
CallSurchargeCodeSize(len) => s.call_per_code_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
|
||||
.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())),
|
||||
InstantiateSurchargeCodeSize(len) =>
|
||||
s.instantiate_per_code_byte.saturating_mul(len.into()),
|
||||
InstantiateCopyOut(len) => s.instantiate_per_output_byte
|
||||
.saturating_mul(len.into()),
|
||||
HashSha256(len) => s.hash_sha2_256
|
||||
@@ -291,7 +273,6 @@ impl RuntimeCosts {
|
||||
HashBlake128(len) => s.hash_blake2_128
|
||||
.saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())),
|
||||
ChainExtension(amount) => amount,
|
||||
CopyIn(len) => s.return_per_byte.saturating_mul(len.into()),
|
||||
};
|
||||
RuntimeToken {
|
||||
#[cfg(test)]
|
||||
@@ -476,15 +457,6 @@ where
|
||||
self.ext.gas_meter().charge(token)
|
||||
}
|
||||
|
||||
/// Correct previously charged gas amount.
|
||||
pub fn adjust_gas(&mut self, charged_amount: ChargedAmount, adjusted_amount: RuntimeCosts) {
|
||||
let adjusted_amount = adjusted_amount.token(&self.ext.schedule().host_fn_weights);
|
||||
self.ext.gas_meter().adjust_gas(
|
||||
charged_amount,
|
||||
adjusted_amount,
|
||||
);
|
||||
}
|
||||
|
||||
/// Read designated chunk from the sandbox memory.
|
||||
///
|
||||
/// Returns `Err` if one of the following conditions occurs:
|
||||
@@ -511,6 +483,21 @@ where
|
||||
self.memory.get(ptr, buf).map_err(|_| Error::<E::T>::OutOfBounds.into())
|
||||
}
|
||||
|
||||
/// Reads and decodes a type with a size fixed at compile time from contract memory.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// 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>
|
||||
{
|
||||
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))?;
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
/// Read designated chunk from the sandbox memory and attempt to decode into the specified type.
|
||||
///
|
||||
/// Returns `Err` if one of the following conditions occurs:
|
||||
@@ -520,25 +507,14 @@ where
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// It is safe to forgo benchmarking and charging weight relative to `len` for fixed
|
||||
/// size types (basically everything not containing a heap collection):
|
||||
/// Despite the fact that we are usually about to read the encoding of a fixed size
|
||||
/// type, we cannot know the encoded size of that type. We therefore are required to
|
||||
/// use the length provided by the contract. This length is untrusted and therefore
|
||||
/// we charge weight relative to the provided size upfront that covers the copy costs.
|
||||
/// On success this cost is refunded as the copying was already covered in the
|
||||
/// overall cost of the host function. This is different from `read_sandbox_memory`
|
||||
/// where the size is dynamic and the costs resulting from that dynamic size must
|
||||
/// be charged relative to this dynamic size anyways (before reading) by constructing
|
||||
/// the benchmark for that.
|
||||
pub fn read_sandbox_memory_as<D: Decode>(&mut self, ptr: u32, len: u32)
|
||||
/// 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>
|
||||
{
|
||||
let amount = self.charge_gas(RuntimeCosts::CopyIn(len))?;
|
||||
let buf = self.read_sandbox_memory(ptr, len)?;
|
||||
let decoded = D::decode_all(&mut &buf[..])
|
||||
.map_err(|_| DispatchError::from(Error::<E::T>::DecodingFailed))?;
|
||||
self.ext.gas_meter().refund(amount);
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
@@ -575,7 +551,7 @@ where
|
||||
}
|
||||
|
||||
let buf_len = buf.len() as u32;
|
||||
let len: u32 = self.read_sandbox_memory_as(out_len_ptr, 4)?;
|
||||
let len: u32 = self.read_sandbox_memory_as(out_len_ptr)?;
|
||||
|
||||
if len < buf_len {
|
||||
Err(Error::<E::T>::OutputBufferTooSmall)?
|
||||
@@ -675,19 +651,18 @@ where
|
||||
&mut self,
|
||||
flags: CallFlags,
|
||||
callee_ptr: u32,
|
||||
callee_len: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
value_len: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
) -> 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, callee_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = self.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
self.read_sandbox_memory_as(callee_ptr)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = self.read_sandbox_memory_as(value_ptr)?;
|
||||
let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
|
||||
self.input_data.as_ref().ok_or_else(|| Error::<E::T>::InputForwarded)?.clone()
|
||||
} else if flags.contains(CallFlags::FORWARD_INPUT) {
|
||||
@@ -698,23 +673,15 @@ where
|
||||
if value > 0u32.into() {
|
||||
self.charge_gas(RuntimeCosts::CallSurchargeTransfer)?;
|
||||
}
|
||||
let charged = self.charge_gas(
|
||||
RuntimeCosts::CallSurchargeCodeSize(<E::T as Config>::Schedule::get().limits.code_len)
|
||||
)?;
|
||||
let ext = &mut self.ext;
|
||||
let call_outcome = ext.call(
|
||||
gas, callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY),
|
||||
);
|
||||
let code_len = match &call_outcome {
|
||||
Ok((_, len)) => len,
|
||||
Err((_, len)) => len,
|
||||
};
|
||||
self.adjust_gas(charged, RuntimeCosts::CallSurchargeCodeSize(*code_len));
|
||||
|
||||
// `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to
|
||||
// a halt anyways without anymore code being executed.
|
||||
if flags.contains(CallFlags::TAIL_CALL) {
|
||||
if let Ok((return_value, _)) = call_outcome {
|
||||
if let Ok(return_value) = call_outcome {
|
||||
return Err(TrapReason::Return(ReturnData {
|
||||
flags: return_value.flags.bits(),
|
||||
data: return_value.data.0,
|
||||
@@ -722,12 +689,98 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok((output, _)) = &call_outcome {
|
||||
if let Ok(output) = &call_outcome {
|
||||
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeCosts::CallCopyOut(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(call_outcome.map(|r| r.0).map_err(|r| r.0))?)
|
||||
Ok(Runtime::<E>::exec_into_return_code(call_outcome)?)
|
||||
}
|
||||
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
code_hash_ptr: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
address_ptr: u32,
|
||||
address_len_ptr: u32,
|
||||
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})?;
|
||||
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)?;
|
||||
let salt = self.read_sandbox_memory(salt_ptr, salt_len)?;
|
||||
let instantiate_outcome = self.ext.instantiate(gas, code_hash, value, input_data, &salt);
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
self.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeCosts::InstantiateCopyOut(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?)
|
||||
}
|
||||
|
||||
fn terminate(&mut self, beneficiary_ptr: u32) -> Result<(), TrapReason> {
|
||||
self.charge_gas(RuntimeCosts::Terminate)?;
|
||||
let beneficiary: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
self.read_sandbox_memory_as(beneficiary_ptr)?;
|
||||
self.ext.terminate(&beneficiary)?;
|
||||
Err(TrapReason::Termination)
|
||||
}
|
||||
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest_ptr: u32,
|
||||
code_hash_ptr: u32,
|
||||
rent_allowance_ptr: u32,
|
||||
delta_ptr: 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 rent_allowance: BalanceOf<<E as Ext>::T> =
|
||||
self.read_sandbox_memory_as(rent_allowance_ptr)?;
|
||||
let delta = {
|
||||
const KEY_SIZE: usize = 32;
|
||||
|
||||
// We can eagerly allocate because we charged for the complete delta count already
|
||||
// We still need to make sure that the allocation isn't larger than the memory
|
||||
// allocator can handle.
|
||||
let max_memory = self.ext.schedule().limits.max_memory_size();
|
||||
ensure!(
|
||||
delta_count.saturating_mul(KEY_SIZE as u32) <= max_memory,
|
||||
Error::<E::T>::OutOfBounds,
|
||||
);
|
||||
let mut delta = vec![[0; KEY_SIZE]; delta_count as usize];
|
||||
let mut key_ptr = delta_ptr;
|
||||
|
||||
for i in 0..delta_count {
|
||||
// Read the delta into the provided buffer
|
||||
// This cannot panic because of the loop condition
|
||||
self.read_sandbox_memory_into_buf(key_ptr, &mut delta[i as usize])?;
|
||||
|
||||
// Offset key_ptr to the next element.
|
||||
key_ptr = key_ptr.checked_add(KEY_SIZE as u32).ok_or(Error::<E::T>::OutOfBounds)?;
|
||||
}
|
||||
|
||||
delta
|
||||
};
|
||||
self.ext.restore_to(dest, code_hash, rent_allowance, delta)?;
|
||||
Err(TrapReason::Restoration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,15 +891,15 @@ define_env!(Env, <E: Ext>,
|
||||
[seal0] seal_transfer(
|
||||
ctx,
|
||||
account_ptr: u32,
|
||||
account_len: u32,
|
||||
_account_len: u32,
|
||||
value_ptr: u32,
|
||||
value_len: u32
|
||||
_value_len: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeCosts::Transfer)?;
|
||||
let callee: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(account_ptr, account_len)?;
|
||||
ctx.read_sandbox_memory_as(account_ptr)?;
|
||||
let value: BalanceOf<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
ctx.read_sandbox_memory_as(value_ptr)?;
|
||||
|
||||
let result = ctx.ext.transfer(&callee, value);
|
||||
match result {
|
||||
@@ -860,15 +913,23 @@ define_env!(Env, <E: Ext>,
|
||||
|
||||
// Make a call to another contract.
|
||||
//
|
||||
// # Deprecation
|
||||
//
|
||||
// This is equivalent to calling the newer version of this function with
|
||||
// `flags` set to `ALLOW_REENTRY`. See the newer version for documentation.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// The values `_callee_len` and `_value_len` are ignored because the encoded sizes
|
||||
// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards
|
||||
// compatibility. Consider switching to the newest version of this function.
|
||||
[seal0] seal_call(
|
||||
ctx,
|
||||
callee_ptr: u32,
|
||||
callee_len: u32,
|
||||
_callee_len: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
value_len: u32,
|
||||
_value_len: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
output_ptr: u32,
|
||||
@@ -877,10 +938,8 @@ define_env!(Env, <E: Ext>,
|
||||
ctx.call(
|
||||
CallFlags::ALLOW_REENTRY,
|
||||
callee_ptr,
|
||||
callee_len,
|
||||
gas,
|
||||
value_ptr,
|
||||
value_len,
|
||||
input_data_ptr,
|
||||
input_data_len,
|
||||
output_ptr,
|
||||
@@ -899,11 +958,9 @@ define_env!(Env, <E: Ext>,
|
||||
// - flags: See [`CallFlags`] for a documenation of the supported flags.
|
||||
// - callee_ptr: a pointer to the address of the callee contract.
|
||||
// Should be decodable as an `T::AccountId`. Traps otherwise.
|
||||
// - callee_len: length of the address buffer.
|
||||
// - gas: how much gas to devote to the execution.
|
||||
// - value_ptr: a pointer to the buffer with value, how much value to send.
|
||||
// Should be decodable as a `T::Balance`. Traps otherwise.
|
||||
// - value_len: length of the value buffer.
|
||||
// - input_data_ptr: a pointer to a buffer to be used as input data to the callee.
|
||||
// - input_data_len: length of the input data buffer.
|
||||
// - output_ptr: a pointer where the output buffer is copied to.
|
||||
@@ -924,10 +981,8 @@ define_env!(Env, <E: Ext>,
|
||||
ctx,
|
||||
flags: u32,
|
||||
callee_ptr: u32,
|
||||
callee_len: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
value_len: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
output_ptr: u32,
|
||||
@@ -936,10 +991,8 @@ define_env!(Env, <E: Ext>,
|
||||
ctx.call(
|
||||
CallFlags::from_bits(flags).ok_or_else(|| "used rerved bit in CallFlags")?,
|
||||
callee_ptr,
|
||||
callee_len,
|
||||
gas,
|
||||
value_ptr,
|
||||
value_len,
|
||||
input_data_ptr,
|
||||
input_data_len,
|
||||
output_ptr,
|
||||
@@ -947,6 +1000,49 @@ define_env!(Env, <E: Ext>,
|
||||
)
|
||||
},
|
||||
|
||||
// Instantiate a contract with the specified code hash.
|
||||
//
|
||||
// # Deprecation
|
||||
//
|
||||
// This is equivalent to calling the newer version of this function. The newer version
|
||||
// drops the now unnecessary length fields.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes
|
||||
// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards
|
||||
// compatibility. Consider switching to the newest version of this function.
|
||||
[seal0] seal_instantiate(
|
||||
ctx,
|
||||
code_hash_ptr: u32,
|
||||
_code_hash_len: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
_value_len: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
address_ptr: u32,
|
||||
address_len_ptr: u32,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32,
|
||||
salt_ptr: u32,
|
||||
salt_len: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.instantiate (
|
||||
code_hash_ptr,
|
||||
gas,
|
||||
value_ptr,
|
||||
input_data_ptr,
|
||||
input_data_len,
|
||||
address_ptr,
|
||||
address_len_ptr,
|
||||
output_ptr,
|
||||
output_len_ptr,
|
||||
salt_ptr,
|
||||
salt_len,
|
||||
)
|
||||
},
|
||||
|
||||
// Instantiate a contract with the specified code hash.
|
||||
//
|
||||
// This function creates an account and executes the constructor defined in the code specified
|
||||
@@ -962,11 +1058,9 @@ define_env!(Env, <E: Ext>,
|
||||
// # Parameters
|
||||
//
|
||||
// - code_hash_ptr: a pointer to the buffer that contains the initializer code.
|
||||
// - code_hash_len: length of the initializer code buffer.
|
||||
// - gas: how much gas to devote to the execution of the initializer code.
|
||||
// - value_ptr: a pointer to the buffer with value, how much value to send.
|
||||
// Should be decodable as a `T::Balance`. Traps otherwise.
|
||||
// - value_len: length of the value buffer.
|
||||
// - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code.
|
||||
// - input_data_len: length of the input data buffer.
|
||||
// - address_ptr: a pointer where the new account's address is copied to.
|
||||
@@ -992,13 +1086,11 @@ define_env!(Env, <E: Ext>,
|
||||
// `ReturnCode::TransferFailed`
|
||||
// `ReturnCode::NewContractNotFunded`
|
||||
// `ReturnCode::CodeNotFound`
|
||||
[seal0] seal_instantiate(
|
||||
[seal1] seal_instantiate(
|
||||
ctx,
|
||||
code_hash_ptr: u32,
|
||||
code_hash_len: u32,
|
||||
gas: u64,
|
||||
value_ptr: u32,
|
||||
value_len: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
address_ptr: u32,
|
||||
@@ -1008,37 +1100,35 @@ define_env!(Env, <E: Ext>,
|
||||
salt_ptr: u32,
|
||||
salt_len: u32
|
||||
) -> ReturnCode => {
|
||||
ctx.charge_gas(RuntimeCosts::InstantiateBase {input_data_len, salt_len})?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(code_hash_ptr, code_hash_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
|
||||
let salt = ctx.read_sandbox_memory(salt_ptr, salt_len)?;
|
||||
let charged = ctx.charge_gas(
|
||||
RuntimeCosts::InstantiateSurchargeCodeSize(
|
||||
<E::T as Config>::Schedule::get().limits.code_len
|
||||
)
|
||||
)?;
|
||||
let ext = &mut ctx.ext;
|
||||
let instantiate_outcome = ext.instantiate(gas, code_hash, value, input_data, &salt);
|
||||
let code_len = match &instantiate_outcome {
|
||||
Ok((_, _, code_len)) => code_len,
|
||||
Err((_, code_len)) => code_len,
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeCosts::InstantiateSurchargeCodeSize(*code_len));
|
||||
if let Ok((address, output, _)) = &instantiate_outcome {
|
||||
if !output.flags.contains(ReturnFlags::REVERT) {
|
||||
ctx.write_sandbox_output(
|
||||
address_ptr, address_len_ptr, &address.encode(), true, already_charged,
|
||||
)?;
|
||||
}
|
||||
ctx.write_sandbox_output(output_ptr, output_len_ptr, &output.data, true, |len| {
|
||||
Some(RuntimeCosts::InstantiateCopyOut(len))
|
||||
})?;
|
||||
}
|
||||
Ok(Runtime::<E>::exec_into_return_code(
|
||||
instantiate_outcome.map(|(_, retval, _)| retval).map_err(|(err, _)| err)
|
||||
)?)
|
||||
ctx.instantiate(
|
||||
code_hash_ptr,
|
||||
gas,
|
||||
value_ptr,
|
||||
input_data_ptr,
|
||||
input_data_len,
|
||||
address_ptr,
|
||||
address_len_ptr,
|
||||
output_ptr,
|
||||
output_len_ptr,
|
||||
salt_ptr,
|
||||
salt_len,
|
||||
)
|
||||
},
|
||||
|
||||
// Remove the calling account and transfer remaining balance.
|
||||
//
|
||||
// # Deprecation
|
||||
//
|
||||
// This is equivalent to calling the newer version of this function. The newer version
|
||||
// drops the now unnecessary length fields.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// The value `_beneficiary_len` is ignored because the encoded sizes
|
||||
// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards
|
||||
// compatibility. Consider switching to the newest version of this function.
|
||||
[seal0] seal_terminate(ctx, beneficiary_ptr: u32, _beneficiary_len: u32) => {
|
||||
ctx.terminate(beneficiary_ptr)
|
||||
},
|
||||
|
||||
// Remove the calling account and transfer remaining balance.
|
||||
@@ -1050,33 +1140,14 @@ define_env!(Env, <E: Ext>,
|
||||
// - beneficiary_ptr: a pointer to the address of the beneficiary account where all
|
||||
// where all remaining funds of the caller are transferred.
|
||||
// Should be decodable as an `T::AccountId`. Traps otherwise.
|
||||
// - beneficiary_len: length of the address buffer.
|
||||
//
|
||||
// # Traps
|
||||
//
|
||||
// - The contract is live i.e is already on the call stack.
|
||||
// - Failed to send the balance to the beneficiary.
|
||||
// - The deletion queue is full.
|
||||
[seal0] seal_terminate(
|
||||
ctx,
|
||||
beneficiary_ptr: u32,
|
||||
beneficiary_len: u32
|
||||
) => {
|
||||
ctx.charge_gas(RuntimeCosts::Terminate)?;
|
||||
let beneficiary: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(beneficiary_ptr, beneficiary_len)?;
|
||||
let charged = ctx.charge_gas(
|
||||
RuntimeCosts::TerminateSurchargeCodeSize(
|
||||
<E::T as Config>::Schedule::get().limits.code_len
|
||||
)
|
||||
)?;
|
||||
let (result, code_len) = match ctx.ext.terminate(&beneficiary) {
|
||||
Ok(len) => (Ok(()), len),
|
||||
Err((err, len)) => (Err(err), len),
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeCosts::TerminateSurchargeCodeSize(code_len));
|
||||
result?;
|
||||
Err(TrapReason::Termination)
|
||||
[seal1] seal_terminate(ctx, beneficiary_ptr: u32) => {
|
||||
ctx.terminate(beneficiary_ptr)
|
||||
},
|
||||
|
||||
// Stores the input passed by the caller into the supplied buffer.
|
||||
@@ -1323,6 +1394,38 @@ define_env!(Env, <E: Ext>,
|
||||
)?)
|
||||
},
|
||||
|
||||
// Try to restore the given destination contract sacrificing the caller.
|
||||
//
|
||||
// # Deprecation
|
||||
//
|
||||
// This is equivalent to calling the newer version of this function. The newer version
|
||||
// drops the now unnecessary length fields.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// The values `_dest_len`, `_code_hash_len` and `_rent_allowance_len` are ignored because
|
||||
// the encoded sizes of those types are fixed through `[`MaxEncodedLen`]. The fields
|
||||
// exist for backwards compatibility. Consider switching to the newest version of this function.
|
||||
[seal0] seal_restore_to(
|
||||
ctx,
|
||||
dest_ptr: u32,
|
||||
_dest_len: u32,
|
||||
code_hash_ptr: u32,
|
||||
_code_hash_len: u32,
|
||||
rent_allowance_ptr: u32,
|
||||
_rent_allowance_len: u32,
|
||||
delta_ptr: u32,
|
||||
delta_count: u32
|
||||
) => {
|
||||
ctx.restore_to(
|
||||
dest_ptr,
|
||||
code_hash_ptr,
|
||||
rent_allowance_ptr,
|
||||
delta_ptr,
|
||||
delta_count,
|
||||
)
|
||||
},
|
||||
|
||||
// Try to restore the given destination contract sacrificing the caller.
|
||||
//
|
||||
// This function will compute a tombstone hash from the caller's storage and the given code hash
|
||||
@@ -1339,11 +1442,11 @@ define_env!(Env, <E: Ext>,
|
||||
// On success, the destination contract is restored. This function is diverging and
|
||||
// stops execution even on success.
|
||||
//
|
||||
// - `dest_ptr`, `dest_len` - the pointer and the length of a buffer that encodes `T::AccountId`
|
||||
// - `dest_ptr` - the pointer to a buffer that encodes `T::AccountId`
|
||||
// with the address of the to be restored contract.
|
||||
// - `code_hash_ptr`, `code_hash_len` - the pointer and the length of a buffer that encodes
|
||||
// - `code_hash_ptr` - the pointer to a buffer that encodes
|
||||
// a code hash of the to be restored contract.
|
||||
// - `rent_allowance_ptr`, `rent_allowance_len` - the pointer and the length of a buffer that
|
||||
// - `rent_allowance_ptr` - the pointer to a buffer that
|
||||
// encodes the rent allowance that must be set in the case of successful restoration.
|
||||
// - `delta_ptr` is the pointer to the start of a buffer that has `delta_count` storage keys
|
||||
// laid out sequentially.
|
||||
@@ -1354,67 +1457,21 @@ define_env!(Env, <E: Ext>,
|
||||
// - Tombstone hashes do not match.
|
||||
// - The calling contract is already present on the call stack.
|
||||
// - The supplied code_hash does not exist on-chain.
|
||||
[seal0] seal_restore_to(
|
||||
[seal1] seal_restore_to(
|
||||
ctx,
|
||||
dest_ptr: u32,
|
||||
dest_len: u32,
|
||||
code_hash_ptr: u32,
|
||||
code_hash_len: u32,
|
||||
rent_allowance_ptr: u32,
|
||||
rent_allowance_len: u32,
|
||||
delta_ptr: u32,
|
||||
delta_count: u32
|
||||
) => {
|
||||
ctx.charge_gas(RuntimeCosts::RestoreTo(delta_count))?;
|
||||
let dest: <<E as Ext>::T as frame_system::Config>::AccountId =
|
||||
ctx.read_sandbox_memory_as(dest_ptr, dest_len)?;
|
||||
let code_hash: CodeHash<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(code_hash_ptr, code_hash_len)?;
|
||||
let rent_allowance: BalanceOf<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(rent_allowance_ptr, rent_allowance_len)?;
|
||||
let delta = {
|
||||
const KEY_SIZE: usize = 32;
|
||||
|
||||
// We can eagerly allocate because we charged for the complete delta count already
|
||||
// We still need to make sure that the allocation isn't larger than the memory
|
||||
// allocator can handle.
|
||||
ensure!(
|
||||
delta_count
|
||||
.saturating_mul(KEY_SIZE as u32) <= ctx.ext.schedule().limits.max_memory_size(),
|
||||
Error::<E::T>::OutOfBounds,
|
||||
);
|
||||
let mut delta = vec![[0; KEY_SIZE]; delta_count as usize];
|
||||
let mut key_ptr = delta_ptr;
|
||||
|
||||
for i in 0..delta_count {
|
||||
// Read the delta into the provided buffer
|
||||
// This cannot panic because of the loop condition
|
||||
ctx.read_sandbox_memory_into_buf(key_ptr, &mut delta[i as usize])?;
|
||||
|
||||
// Offset key_ptr to the next element.
|
||||
key_ptr = key_ptr.checked_add(KEY_SIZE as u32).ok_or(Error::<E::T>::OutOfBounds)?;
|
||||
}
|
||||
|
||||
delta
|
||||
};
|
||||
|
||||
let max_len = <E::T as Config>::Schedule::get().limits.code_len;
|
||||
let charged = ctx.charge_gas(RuntimeCosts::RestoreToSurchargeCodeSize {
|
||||
caller_code: max_len,
|
||||
tombstone_code: max_len,
|
||||
})?;
|
||||
let (result, caller_code, tombstone_code) = match ctx.ext.restore_to(
|
||||
dest, code_hash, rent_allowance, delta
|
||||
) {
|
||||
Ok((code, tomb)) => (Ok(()), code, tomb),
|
||||
Err((err, code, tomb)) => (Err(err), code, tomb),
|
||||
};
|
||||
ctx.adjust_gas(charged, RuntimeCosts::RestoreToSurchargeCodeSize {
|
||||
caller_code,
|
||||
tombstone_code,
|
||||
});
|
||||
result?;
|
||||
Err(TrapReason::Restoration)
|
||||
ctx.restore_to(
|
||||
dest_ptr,
|
||||
code_hash_ptr,
|
||||
rent_allowance_ptr,
|
||||
delta_ptr,
|
||||
delta_count,
|
||||
)
|
||||
},
|
||||
|
||||
// Deposit a contract event with the data buffer and optional list of topics. There is a limit
|
||||
@@ -1460,7 +1517,7 @@ define_env!(Env, <E: Ext>,
|
||||
|
||||
let mut topics: Vec::<TopicOf<<E as Ext>::T>> = match topics_len {
|
||||
0 => Vec::new(),
|
||||
_ => ctx.read_sandbox_memory_as(topics_ptr, topics_len)?,
|
||||
_ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?,
|
||||
};
|
||||
|
||||
// If there are more than `event_topics`, then trap.
|
||||
@@ -1482,17 +1539,33 @@ define_env!(Env, <E: Ext>,
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Set rent allowance of the contract
|
||||
// Set rent allowance of the contract.
|
||||
//
|
||||
// # Deprecation
|
||||
//
|
||||
// This is equivalent to calling the newer version of this function. The newer version
|
||||
// drops the now unnecessary length fields.
|
||||
//
|
||||
// # Note
|
||||
//
|
||||
// The value `_VALUE_len` is ignored because the encoded sizes
|
||||
// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards
|
||||
// compatibility. Consider switching to the newest version of this function.
|
||||
[seal0] seal_set_rent_allowance(ctx, value_ptr: u32, _value_len: u32) => {
|
||||
ctx.charge_gas(RuntimeCosts::SetRentAllowance)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr)?;
|
||||
ctx.ext.set_rent_allowance(value);
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Set rent allowance of the contract.
|
||||
//
|
||||
// - value_ptr: a pointer to the buffer with value, how much to allow for rent
|
||||
// Should be decodable as a `T::Balance`. Traps otherwise.
|
||||
// - value_len: length of the value buffer.
|
||||
[seal0] seal_set_rent_allowance(ctx, value_ptr: u32, value_len: u32) => {
|
||||
[seal1] seal_set_rent_allowance(ctx, value_ptr: u32) => {
|
||||
ctx.charge_gas(RuntimeCosts::SetRentAllowance)?;
|
||||
let value: BalanceOf<<E as Ext>::T> =
|
||||
ctx.read_sandbox_memory_as(value_ptr, value_len)?;
|
||||
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr)?;
|
||||
ctx.ext.set_rent_allowance(value);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user