contracts: Get rid of #[pallet::without_storage_info] (#11414)

* Implement `MaxEncodeLen` for pallet-contracts storage

* Remove redundant debug println

* Move code len check to PrefabWasmModule::from_code
This commit is contained in:
Alexander Theißen
2022-05-18 09:40:53 +02:00
committed by GitHub
parent fe4acb7924
commit ef46d84aed
9 changed files with 99 additions and 67 deletions
@@ -163,7 +163,8 @@ pub fn load<T: Config>(
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
let charged = gas_meter.charge(CodeToken::Load(schedule.limits.code_len))?;
let max_code_len = T::MaxCodeLen::get();
let charged = gas_meter.charge(CodeToken::Load(max_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));
@@ -172,7 +173,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.
let charged = gas_meter.charge(CodeToken::Reinstrument(schedule.limits.code_len))?;
let charged = gas_meter.charge(CodeToken::Reinstrument(max_code_len))?;
let code_size = reinstrument(&mut prefab_module, schedule)?;
gas_meter.adjust_gas(charged, CodeToken::Reinstrument(code_size));
}
@@ -190,7 +191,10 @@ pub fn reinstrument<T: Config>(
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.code = prepare::reinstrument_contract::<T>(&original_code, schedule)
.map_err(|_| <Error<T>>::CodeRejected)?
.try_into()
.map_err(|_| <Error<T>>::CodeTooLarge)?;
prefab_module.instruction_weights_version = schedule.instruction_weights.version;
<CodeStorage<T>>::insert(&prefab_module.code_hash, &*prefab_module);
Ok(original_code_len as u32)
+24 -7
View File
@@ -31,10 +31,15 @@ use crate::{
exec::{ExecResult, Executable, ExportedFunction, Ext},
gas::GasMeter,
wasm::env_def::FunctionImplProvider,
AccountIdOf, BalanceOf, CodeHash, CodeStorage, Config, Schedule,
AccountIdOf, BalanceOf, CodeHash, CodeStorage, CodeVec, Config, Error, RelaxedCodeVec,
Schedule,
};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::dispatch::{DispatchError, DispatchResult};
use frame_support::{
dispatch::{DispatchError, DispatchResult},
ensure,
traits::Get,
};
use sp_core::crypto::UncheckedFrom;
use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory};
use sp_std::prelude::*;
@@ -50,7 +55,8 @@ pub use tests::MockExt;
/// `instruction_weights_version` and `code` change when a contract with an outdated instrumentation
/// is called. Therefore one must be careful when holding any in-memory representation of this
/// type while calling into a contract as those fields can get out of date.
#[derive(Clone, Encode, Decode, scale_info::TypeInfo)]
#[derive(Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)]
#[codec(mel_bound())]
#[scale_info(skip_type_params(T))]
pub struct PrefabWasmModule<T: Config> {
/// Version of the instruction weights with which the code was instrumented.
@@ -63,14 +69,14 @@ pub struct PrefabWasmModule<T: Config> {
#[codec(compact)]
maximum: u32,
/// Code instrumented with the latest schedule.
code: Vec<u8>,
code: RelaxedCodeVec<T>,
/// The uninstrumented, pristine version of the code.
///
/// It is not stored because the pristine code has its own storage item. The value
/// is only `Some` when this module was created from an `original_code` and `None` if
/// it was loaded from storage.
#[codec(skip)]
original_code: Option<Vec<u8>>,
original_code: Option<CodeVec<T>>,
/// The code hash of the stored code which is defined as the hash over the `original_code`.
///
/// As the map key there is no need to store the hash in the value, too. It is set manually
@@ -122,8 +128,19 @@ where
original_code: Vec<u8>,
schedule: &Schedule<T>,
owner: AccountIdOf<T>,
) -> Result<Self, &'static str> {
prepare::prepare_contract(original_code, schedule, owner)
) -> Result<Self, (DispatchError, &'static str)> {
let module = prepare::prepare_contract(
original_code.try_into().map_err(|_| (<Error<T>>::CodeTooLarge.into(), ""))?,
schedule,
owner,
)?;
// When instrumenting a new code we apply a stricter limit than enforced by the
// `RelaxedCodeVec` in order to leave some headroom for reinstrumentation.
ensure!(
module.code.len() as u32 <= T::MaxCodeLen::get(),
(<Error<T>>::CodeTooLarge.into(), ""),
);
Ok(module)
}
/// Store the code without instantiating it.
+18 -15
View File
@@ -23,10 +23,10 @@ use crate::{
chain_extension::ChainExtension,
storage::meter::Diff,
wasm::{env_def::ImportSatisfyCheck, OwnerInfo, PrefabWasmModule},
AccountIdOf, Config, Schedule,
AccountIdOf, CodeVec, Config, Error, Schedule,
};
use codec::{Encode, MaxEncodedLen};
use sp_runtime::traits::Hash;
use sp_runtime::{traits::Hash, DispatchError};
use sp_std::prelude::*;
use wasm_instrument::parity_wasm::elements::{
self, External, Internal, MemoryType, Type, ValueType,
@@ -401,19 +401,19 @@ fn check_and_instrument<C: ImportSatisfyCheck, T: Config>(
}
fn do_preparation<C: ImportSatisfyCheck, T: Config>(
original_code: Vec<u8>,
original_code: CodeVec<T>,
schedule: &Schedule<T>,
owner: AccountIdOf<T>,
) -> Result<PrefabWasmModule<T>, &'static str> {
let (code, (initial, maximum)) =
check_and_instrument::<C, T>(original_code.as_ref(), schedule)?;
) -> Result<PrefabWasmModule<T>, (DispatchError, &'static str)> {
let (code, (initial, maximum)) = check_and_instrument::<C, T>(original_code.as_ref(), schedule)
.map_err(|msg| (<Error<T>>::CodeRejected.into(), msg))?;
let original_code_len = original_code.len();
let mut module = PrefabWasmModule {
instruction_weights_version: schedule.instruction_weights.version,
initial,
maximum,
code,
code: code.try_into().map_err(|_| (<Error<T>>::CodeTooLarge.into(), ""))?,
code_hash: T::Hashing::hash(&original_code),
original_code: Some(original_code),
owner_info: None,
@@ -446,10 +446,10 @@ fn do_preparation<C: ImportSatisfyCheck, T: Config>(
///
/// The preprocessing includes injecting code for gas metering and metering the height of stack.
pub fn prepare_contract<T: Config>(
original_code: Vec<u8>,
original_code: CodeVec<T>,
schedule: &Schedule<T>,
owner: AccountIdOf<T>,
) -> Result<PrefabWasmModule<T>, &'static str> {
) -> Result<PrefabWasmModule<T>, (DispatchError, &'static str)> {
do_preparation::<super::runtime::Env, T>(original_code, schedule, owner)
}
@@ -459,10 +459,10 @@ pub fn prepare_contract<T: Config>(
///
/// Use this when an existing contract should be re-instrumented with a newer schedule version.
pub fn reinstrument_contract<T: Config>(
original_code: Vec<u8>,
original_code: &[u8],
schedule: &Schedule<T>,
) -> Result<Vec<u8>, &'static str> {
Ok(check_and_instrument::<super::runtime::Env, T>(&original_code, schedule)?.0)
Ok(check_and_instrument::<super::runtime::Env, T>(original_code, schedule)?.0)
}
/// Alternate (possibly unsafe) preparation functions used only for benchmarking.
@@ -493,9 +493,12 @@ pub mod benchmarking {
instruction_weights_version: schedule.instruction_weights.version,
initial: memory_limits.0,
maximum: memory_limits.1,
code: contract_module.into_wasm_code()?,
code_hash: T::Hashing::hash(&original_code),
original_code: Some(original_code),
original_code: Some(original_code.try_into().map_err(|_| "Original code too large")?),
code: contract_module
.into_wasm_code()?
.try_into()
.map_err(|_| "Instrumented code too large")?,
owner_info: Some(OwnerInfo {
owner,
// this is a helper function for benchmarking which skips deposit collection
@@ -546,7 +549,7 @@ mod tests {
($name:ident, $wat:expr, $($expected:tt)*) => {
#[test]
fn $name() {
let wasm = wat::parse_str($wat).unwrap();
let wasm = wat::parse_str($wat).unwrap().try_into().unwrap();
let schedule = Schedule {
limits: Limits {
globals: 3,
@@ -559,7 +562,7 @@ mod tests {
.. Default::default()
};
let r = do_preparation::<env::Test, Test>(wasm, &schedule, ALICE);
assert_matches::assert_matches!(r, $($expected)*);
assert_matches::assert_matches!(r.map_err(|(_, msg)| msg), $($expected)*);
}
};
}