mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 16:21:06 +00:00
b581604aa7
* Apply some clippy hints * Revert clippy ci changes * Update client/cli/src/commands/generate.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/inspect_key.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/db/src/bench.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/db/src/bench.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/service/src/client/block_rules.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/service/src/client/block_rules.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/network/src/transactions.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/network/src/protocol.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Revert due to missing `or_default` function. * Fix compilation and simplify code * Undo change that corrupts benchmark. * fix clippy * Update client/service/test/src/lib.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/state-db/src/noncanonical.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/state-db/src/noncanonical.rs remove leftovers! * Update client/tracing/src/logging/directives.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update utils/fork-tree/src/lib.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * added needed ref * Update frame/referenda/src/benchmarking.rs * Simplify byte-vec creation * let's just not overlap the ranges * Correction * cargo fmt * Update utils/frame/benchmarking-cli/src/shared/stats.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update utils/frame/benchmarking-cli/src/pallet/command.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update utils/frame/benchmarking-cli/src/pallet/command.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Giles Cope <gilescope@gmail.com>
224 lines
8.4 KiB
Rust
224 lines
8.4 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! A module that implements instrumented code cache.
|
|
//!
|
|
//! - In order to run contract code we need to instrument it with gas metering.
|
|
//! To do that we need to provide the schedule which will supply exact gas costs values.
|
|
//! We cache this code in the storage saving the schedule version.
|
|
//! - Before running contract code we check if the cached code has the schedule version that
|
|
//! is equal to the current saved schedule.
|
|
//! If it is equal then run the code, if it isn't reinstrument with the current schedule.
|
|
//! - When we update the schedule we want it to have strictly greater version than the current saved
|
|
//! one:
|
|
//! this guarantees that every instrumented contract code in cache cannot have the version equal to
|
|
//! the current one. Thus, before executing a contract it should be reinstrument with new schedule.
|
|
|
|
use crate::{
|
|
gas::{GasMeter, Token},
|
|
wasm::{prepare, PrefabWasmModule},
|
|
weights::WeightInfo,
|
|
CodeHash, CodeStorage, Config, Error, Event, OwnerInfoOf, Pallet, PristineCode, Schedule,
|
|
Weight,
|
|
};
|
|
use frame_support::{
|
|
dispatch::{DispatchError, DispatchResult},
|
|
ensure,
|
|
traits::ReservableCurrency,
|
|
};
|
|
use sp_core::crypto::UncheckedFrom;
|
|
use sp_runtime::traits::BadOrigin;
|
|
|
|
/// Put the instrumented module in storage.
|
|
///
|
|
/// Increments the refcount of the in-storage `prefab_module` if it already exists in storage
|
|
/// under the specified `code_hash`.
|
|
pub fn store<T: Config>(mut module: PrefabWasmModule<T>, instantiated: bool) -> DispatchResult
|
|
where
|
|
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
|
{
|
|
let code_hash = sp_std::mem::take(&mut module.code_hash);
|
|
<CodeStorage<T>>::mutate(&code_hash, |existing| match existing {
|
|
Some(existing) => {
|
|
// We instrument any uploaded contract anyways. We might as well store it to save
|
|
// a potential re-instrumentation later.
|
|
existing.code = module.code;
|
|
existing.instruction_weights_version = module.instruction_weights_version;
|
|
// When the code was merely uploaded but not instantiated we can skip this.
|
|
if instantiated {
|
|
<OwnerInfoOf<T>>::mutate(&code_hash, |owner_info| {
|
|
if let Some(owner_info) = owner_info {
|
|
owner_info.refcount = owner_info.refcount.checked_add(1).expect(
|
|
"
|
|
refcount is 64bit. Generating this overflow would require to store
|
|
_at least_ 18 exabyte of data assuming that a contract consumes only
|
|
one byte of data. Any node would run out of storage space before hitting
|
|
this overflow.
|
|
qed
|
|
",
|
|
);
|
|
}
|
|
})
|
|
}
|
|
Ok(())
|
|
},
|
|
None => {
|
|
let orig_code = module.original_code.take().expect(
|
|
"
|
|
If an executable isn't in storage it was uploaded.
|
|
If it was uploaded the original code must exist. qed
|
|
",
|
|
);
|
|
let mut owner_info = module.owner_info.take().expect(
|
|
"If an executable isn't in storage it was uploaded.
|
|
If it was uploaded the owner info was generated and attached. qed
|
|
",
|
|
);
|
|
// This `None` case happens only in freshly uploaded modules. This means that
|
|
// the `owner` is always the origin of the current transaction.
|
|
T::Currency::reserve(&owner_info.owner, owner_info.deposit)
|
|
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
|
|
owner_info.refcount = if instantiated { 1 } else { 0 };
|
|
<PristineCode<T>>::insert(&code_hash, orig_code);
|
|
<OwnerInfoOf<T>>::insert(&code_hash, owner_info);
|
|
*existing = Some(module);
|
|
<Pallet<T>>::deposit_event(Event::CodeStored { code_hash });
|
|
Ok(())
|
|
},
|
|
})
|
|
}
|
|
|
|
/// Decrement the refcount of a code in-storage by one.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// 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>) {
|
|
<OwnerInfoOf<T>>::mutate(code_hash, |existing| {
|
|
if let Some(info) = existing {
|
|
info.refcount = info.refcount.saturating_sub(1);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Increment the refcount of a code in-storage by one.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// [`Error::CodeNotFound`] is returned if the specified `code_hash` does not exist.
|
|
pub fn increment_refcount<T: Config>(code_hash: CodeHash<T>) -> Result<(), DispatchError> {
|
|
<OwnerInfoOf<T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
|
|
if let Some(info) = existing {
|
|
info.refcount = info.refcount.saturating_add(1);
|
|
Ok(())
|
|
} else {
|
|
Err(Error::<T>::CodeNotFound.into())
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Try to remove code together with all associated information.
|
|
pub fn try_remove<T: Config>(origin: &T::AccountId, code_hash: CodeHash<T>) -> DispatchResult {
|
|
<OwnerInfoOf<T>>::try_mutate_exists(&code_hash, |existing| {
|
|
if let Some(owner_info) = existing {
|
|
ensure!(owner_info.refcount == 0, <Error<T>>::CodeInUse);
|
|
ensure!(&owner_info.owner == origin, BadOrigin);
|
|
T::Currency::unreserve(&owner_info.owner, owner_info.deposit);
|
|
*existing = None;
|
|
<PristineCode<T>>::remove(&code_hash);
|
|
<CodeStorage<T>>::remove(&code_hash);
|
|
<Pallet<T>>::deposit_event(Event::CodeRemoved { code_hash });
|
|
Ok(())
|
|
} else {
|
|
Err(<Error<T>>::CodeNotFound.into())
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Load code with the given code hash.
|
|
///
|
|
/// 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.
|
|
pub fn load<T: Config>(
|
|
code_hash: CodeHash<T>,
|
|
schedule: &Schedule<T>,
|
|
gas_meter: &mut GasMeter<T>,
|
|
) -> Result<PrefabWasmModule<T>, DispatchError>
|
|
where
|
|
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
|
{
|
|
let charged = gas_meter.charge(CodeToken::Load(schedule.limits.code_len))?;
|
|
|
|
let mut prefab_module = <CodeStorage<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
|
|
gas_meter.adjust_gas(charged, CodeToken::Load(prefab_module.code.len() as u32));
|
|
prefab_module.code_hash = code_hash;
|
|
|
|
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.
|
|
let charged = gas_meter.charge(CodeToken::Reinstrument(schedule.limits.code_len))?;
|
|
let code_size = reinstrument(&mut prefab_module, schedule)?;
|
|
gas_meter.adjust_gas(charged, CodeToken::Reinstrument(code_size));
|
|
}
|
|
|
|
Ok(prefab_module)
|
|
}
|
|
|
|
/// Instruments the passed prefab wasm module with the supplied schedule.
|
|
///
|
|
/// Returns the size in bytes of the uninstrumented code.
|
|
pub fn reinstrument<T: Config>(
|
|
prefab_module: &mut PrefabWasmModule<T>,
|
|
schedule: &Schedule<T>,
|
|
) -> Result<u32, DispatchError> {
|
|
let original_code =
|
|
<PristineCode<T>>::get(&prefab_module.code_hash).ok_or(Error::<T>::CodeNotFound)?;
|
|
let original_code_len = original_code.len();
|
|
prefab_module.code = prepare::reinstrument_contract::<T>(original_code, schedule)?;
|
|
prefab_module.instruction_weights_version = schedule.instruction_weights.version;
|
|
<CodeStorage<T>>::insert(&prefab_module.code_hash, &*prefab_module);
|
|
Ok(original_code_len as u32)
|
|
}
|
|
|
|
/// Costs for operations that are related to code handling.
|
|
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
|
|
#[derive(Clone, Copy)]
|
|
enum CodeToken {
|
|
/// Weight for reinstrumenting a contract contract of the supplied size in bytes.
|
|
Reinstrument(u32),
|
|
/// Weight for loading a contract per byte.
|
|
Load(u32),
|
|
}
|
|
|
|
impl<T: Config> Token<T> for CodeToken {
|
|
fn weight(&self) -> Weight {
|
|
use self::CodeToken::*;
|
|
// In case of `Load` we already covered the general costs of
|
|
// calling 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 for calling the contract we not know the
|
|
// size of the contract.
|
|
match *self {
|
|
Reinstrument(len) => T::WeightInfo::reinstrument(len),
|
|
Load(len) => T::WeightInfo::call_with_code_per_byte(len)
|
|
.saturating_sub(T::WeightInfo::call_with_code_per_byte(0)),
|
|
}
|
|
}
|
|
}
|