mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
contracts: Charge rent for code storage (#7935)
* contracts: Implement refcounting for wasm code * contracts: Charge rent for code storage * contracts: Fix dispatchables erroneously refunding base costs * Fixed typos in comments. Co-authored-by: Andrew Jones <ascjones@gmail.com> * Remove awkward empty line * Fix more typos in docs * Fix typos in docs Co-authored-by: Andrew Jones <ascjones@gmail.com> * Split up complicated expression Co-authored-by: Andrew Jones <ascjones@gmail.com> * review: Remove unused return value * Fix typos Co-authored-by: Andrew Jones <ascjones@gmail.com> * review: Fix refcount being reset to one on re-instrumentation * Document evictable_code parameter * Make Executable::execute consume and store itself * Added comments about stale values * Disregard struct size in occupied_storage() Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
committed by
GitHub
parent
5569313bd6
commit
8e49a8a6a6
@@ -25,7 +25,7 @@ use crate::{
|
||||
wasm::{PrefabWasmModule, env_def::ImportSatisfyCheck},
|
||||
};
|
||||
use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueType};
|
||||
use pwasm_utils;
|
||||
use sp_runtime::traits::Hash;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Currently, all imported functions must be located inside this module. We might support
|
||||
@@ -407,22 +407,11 @@ fn get_memory_limits<T: Config>(module: Option<&MemoryType>, schedule: &Schedule
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the given module given in `original_code`, performs some checks on it and
|
||||
/// does some preprocessing.
|
||||
///
|
||||
/// The checks are:
|
||||
///
|
||||
/// - provided code is a valid wasm module.
|
||||
/// - the module doesn't define an internal memory instance,
|
||||
/// - imported memory (if any) doesn't reserve more memory than permitted by the `schedule`,
|
||||
/// - all imported functions from the external environment matches defined by `env` module,
|
||||
///
|
||||
/// The preprocessing includes injecting code for gas metering and metering the height of stack.
|
||||
pub fn prepare_contract<C: ImportSatisfyCheck, T: Config>(
|
||||
fn check_and_instrument<C: ImportSatisfyCheck, T: Config>(
|
||||
original_code: &[u8],
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<PrefabWasmModule, &'static str> {
|
||||
let mut contract_module = ContractModule::new(original_code, schedule)?;
|
||||
) -> Result<(Vec<u8>, (u32, u32)), &'static str> {
|
||||
let contract_module = ContractModule::new(&original_code, schedule)?;
|
||||
contract_module.scan_exports()?;
|
||||
contract_module.ensure_no_internal_memory()?;
|
||||
contract_module.ensure_table_size_limit(schedule.limits.table_size)?;
|
||||
@@ -438,19 +427,65 @@ pub fn prepare_contract<C: ImportSatisfyCheck, T: Config>(
|
||||
schedule
|
||||
)?;
|
||||
|
||||
contract_module = contract_module
|
||||
let code = contract_module
|
||||
.inject_gas_metering()?
|
||||
.inject_stack_height_metering()?;
|
||||
.inject_stack_height_metering()?
|
||||
.into_wasm_code()?;
|
||||
|
||||
Ok((code, memory_limits))
|
||||
}
|
||||
|
||||
fn do_preparation<C: ImportSatisfyCheck, T: Config>(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<PrefabWasmModule<T>, &'static str> {
|
||||
let (code, (initial, maximum)) = check_and_instrument::<C, T>(
|
||||
original_code.as_ref(),
|
||||
schedule,
|
||||
)?;
|
||||
Ok(PrefabWasmModule {
|
||||
schedule_version: schedule.version,
|
||||
initial: memory_limits.0,
|
||||
maximum: memory_limits.1,
|
||||
initial,
|
||||
maximum,
|
||||
_reserved: None,
|
||||
code: contract_module.into_wasm_code()?,
|
||||
code,
|
||||
original_code_len: original_code.len() as u32,
|
||||
refcount: 1,
|
||||
code_hash: T::Hashing::hash(&original_code),
|
||||
original_code: Some(original_code),
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads the given module given in `original_code`, performs some checks on it and
|
||||
/// does some preprocessing.
|
||||
///
|
||||
/// The checks are:
|
||||
///
|
||||
/// - provided code is a valid wasm module.
|
||||
/// - the module doesn't define an internal memory instance,
|
||||
/// - imported memory (if any) doesn't reserve more memory than permitted by the `schedule`,
|
||||
/// - all imported functions from the external environment matches defined by `env` module,
|
||||
///
|
||||
/// The preprocessing includes injecting code for gas metering and metering the height of stack.
|
||||
pub fn prepare_contract<T: Config>(
|
||||
original_code: Vec<u8>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<PrefabWasmModule<T>, &'static str> {
|
||||
do_preparation::<super::runtime::Env, T>(original_code, schedule)
|
||||
}
|
||||
|
||||
/// The same as [`prepare_contract`] but without constructing a new [`PrefabWasmModule`]
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// 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>,
|
||||
schedule: &Schedule<T>,
|
||||
) -> Result<Vec<u8>, &'static str> {
|
||||
Ok(check_and_instrument::<super::runtime::Env, T>(&original_code, schedule)?.0)
|
||||
}
|
||||
|
||||
/// Alternate (possibly unsafe) preparation functions used only for benchmarking.
|
||||
///
|
||||
/// For benchmarking we need to construct special contracts that might not pass our
|
||||
@@ -459,9 +494,7 @@ pub fn prepare_contract<C: ImportSatisfyCheck, T: Config>(
|
||||
/// in production code.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub mod benchmarking {
|
||||
use super::{
|
||||
Config, ContractModule, PrefabWasmModule, ImportSatisfyCheck, Schedule, get_memory_limits
|
||||
};
|
||||
use super::*;
|
||||
use parity_wasm::elements::FunctionType;
|
||||
|
||||
impl ImportSatisfyCheck for () {
|
||||
@@ -471,10 +504,10 @@ pub mod benchmarking {
|
||||
}
|
||||
|
||||
/// Prepare function that neither checks nor instruments the passed in code.
|
||||
pub fn prepare_contract<T: Config>(original_code: &[u8], schedule: &Schedule<T>)
|
||||
-> Result<PrefabWasmModule, &'static str>
|
||||
pub fn prepare_contract<T: Config>(original_code: Vec<u8>, schedule: &Schedule<T>)
|
||||
-> Result<PrefabWasmModule<T>, &'static str>
|
||||
{
|
||||
let contract_module = ContractModule::new(original_code, schedule)?;
|
||||
let contract_module = ContractModule::new(&original_code, schedule)?;
|
||||
let memory_limits = get_memory_limits(contract_module.scan_imports::<()>(&[])?, schedule)?;
|
||||
Ok(PrefabWasmModule {
|
||||
schedule_version: schedule.version,
|
||||
@@ -482,6 +515,10 @@ pub mod benchmarking {
|
||||
maximum: memory_limits.1,
|
||||
_reserved: None,
|
||||
code: contract_module.into_wasm_code()?,
|
||||
original_code_len: original_code.len() as u32,
|
||||
refcount: 1,
|
||||
code_hash: T::Hashing::hash(&original_code),
|
||||
original_code: Some(original_code),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -493,7 +530,7 @@ mod tests {
|
||||
use std::fmt;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
impl fmt::Debug for PrefabWasmModule {
|
||||
impl fmt::Debug for PrefabWasmModule<crate::tests::Test> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PreparedContract {{ .. }}")
|
||||
}
|
||||
@@ -534,7 +571,7 @@ mod tests {
|
||||
},
|
||||
.. Default::default()
|
||||
};
|
||||
let r = prepare_contract::<env::Test, crate::tests::Test>(wasm.as_ref(), &schedule);
|
||||
let r = do_preparation::<env::Test, crate::tests::Test>(wasm, &schedule);
|
||||
assert_matches!(r, $($expected)*);
|
||||
}
|
||||
};
|
||||
@@ -945,7 +982,7 @@ mod tests {
|
||||
).unwrap();
|
||||
let mut schedule = Schedule::default();
|
||||
schedule.enable_println = true;
|
||||
let r = prepare_contract::<env::Test, crate::tests::Test>(wasm.as_ref(), &schedule);
|
||||
let r = do_preparation::<env::Test, crate::tests::Test>(wasm, &schedule);
|
||||
assert_matches!(r, Ok(_));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user