contracts: Refactor instantiate with code (#14503)

* wip

* fixes

* rm comment

* join fns

* clippy

* Fix limits

* reduce diff

* fix

* fix

* fix typo

* refactor store to  use self

* refactor run to take self by value

* pass tests

* rm comment

* fixes

* fix typo

* rm

* fix fmt

* clippy

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Update frame/contracts/src/wasm/mod.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Update frame/contracts/src/wasm/mod.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* PR review, rm duplicate increment_refcount

* PR review

* Update frame/contracts/src/wasm/prepare.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Add test for failing storage_deposit

* fix lint

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

---------

Co-authored-by: command-bot <>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
PG Herveou
2023-07-07 15:46:16 +02:00
committed by GitHub
parent 9510ad7310
commit d3ef2badcb
7 changed files with 780 additions and 809 deletions
+19 -84
View File
@@ -27,12 +27,9 @@ pub use crate::wasm::runtime::api_doc;
#[cfg(test)]
pub use tests::MockExt;
pub use crate::wasm::{
prepare::TryInstantiate,
runtime::{
AllowDeprecatedInterface, AllowUnstableInterface, CallFlags, Environment, ReturnCode,
Runtime, RuntimeCosts,
},
pub use crate::wasm::runtime::{
AllowDeprecatedInterface, AllowUnstableInterface, CallFlags, Environment, ReturnCode, Runtime,
RuntimeCosts,
};
use crate::{
@@ -147,33 +144,20 @@ impl<T: Config> Token<T> for CodeLoadToken {
impl<T: Config> WasmBlob<T> {
/// Create the module by checking the `code`.
///
/// This does **not** store the module. For this one need to either call [`Self::store`]
/// or [`<Self as Executable>::execute`][`Executable::execute`].
pub fn from_code(
code: Vec<u8>,
schedule: &Schedule<T>,
owner: AccountIdOf<T>,
determinism: Determinism,
try_instantiate: TryInstantiate,
) -> Result<Self, (DispatchError, &'static str)> {
prepare::prepare::<runtime::Env, T>(
code.try_into().map_err(|_| (<Error<T>>::CodeTooLarge.into(), ""))?,
schedule,
owner,
determinism,
try_instantiate,
)
}
/// Store the code without instantiating it.
///
/// Otherwise the code is stored when [`<Self as Executable>::execute`][`Executable::execute`]
/// is called.
pub fn store(self) -> DispatchResult {
Self::store_code(self, false)
}
/// Remove the code from storage and refund the deposit to its owner.
///
/// Applies all necessary checks before removing the code.
@@ -181,18 +165,6 @@ impl<T: Config> WasmBlob<T> {
Self::try_remove_code(origin, code_hash)
}
/// Returns whether there is a deposit to be paid for this module.
///
/// Returns `0` if the module is already in storage and hence no deposit will
/// be charged for storing it.
pub fn open_deposit(&self, code_info: &CodeInfo<T>) -> BalanceOf<T> {
if <CodeInfoOf<T>>::contains_key(self.code_hash()) {
0u32.into()
} else {
code_info.deposit
}
}
/// Creates and returns an instance of the supplied code.
///
/// This is either used for later executing a contract or for validation of a contract.
@@ -227,7 +199,7 @@ impl<T: Config> WasmBlob<T> {
// Query wasmi for memory limits specified in the module's import entry.
let memory_limits = contract.scan_imports::<T>(schedule)?;
// Here we allocate this memory in the _store_. It allocates _inital_ value, but allows it
// to grow up to maximum number of memory pages, if neccesary.
// to grow up to maximum number of memory pages, if necessary.
let qed = "We checked the limits versus our Schedule,
which specifies the max amount of memory pages
well below u16::MAX; qed";
@@ -250,50 +222,26 @@ impl<T: Config> WasmBlob<T> {
Ok((store, memory, instance))
}
/// Getter method for the code_info.
pub fn code_info(&self) -> &CodeInfo<T> {
&self.code_info
}
/// Put the module blob into storage.
///
/// Increments the reference count of the in-storage `WasmBlob`, if it already exists in
/// storage.
fn store_code(mut module: Self, instantiated: bool) -> DispatchResult {
let code_hash = &module.code_hash().clone();
/// Puts the module blob into storage, and returns the deposit collected for the storage.
pub fn store_code(&mut self) -> Result<BalanceOf<T>, Error<T>> {
let code_hash = *self.code_hash();
<CodeInfoOf<T>>::mutate(code_hash, |stored_code_info| {
match stored_code_info {
// Instantiate existing contract.
Some(stored_code_info) if instantiated => {
stored_code_info.refcount = stored_code_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(())
},
// Contract code is already stored in storage. Nothing to be done here.
Some(_) => Ok(()),
Some(_) => Ok(Default::default()),
// Upload a new contract code.
//
// We need to store the code and its code_info, and collect the deposit.
// This `None` case happens only with freshly uploaded modules. This means that
// the `owner` is always the origin of the current transaction.
None => {
// 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(&module.code_info.owner, module.code_info.deposit)
let deposit = self.code_info.deposit;
T::Currency::reserve(&self.code_info.owner, deposit)
.map_err(|_| <Error<T>>::StorageDepositNotEnoughFunds)?;
module.code_info.refcount = if instantiated { 1 } else { 0 };
<PristineCode<T>>::insert(code_hash, module.code);
*stored_code_info = Some(module.code_info);
<Pallet<T>>::deposit_event(
vec![*code_hash],
Event::CodeStored { code_hash: *code_hash },
);
Ok(())
self.code_info.refcount = 0;
<PristineCode<T>>::insert(code_hash, &self.code);
*stored_code_info = Some(self.code_info.clone());
<Pallet<T>>::deposit_event(vec![code_hash], Event::CodeStored { code_hash });
Ok(deposit)
},
}
})
@@ -331,17 +279,6 @@ impl<T: Config> WasmBlob<T> {
Ok(code)
}
/// See [`Self::from_code_unchecked`].
#[cfg(feature = "runtime-benchmarks")]
pub fn store_code_unchecked(
code: Vec<u8>,
schedule: &Schedule<T>,
owner: T::AccountId,
) -> DispatchResult {
let executable = Self::from_code_unchecked(code, schedule, owner)?;
Self::store_code(executable, false)
}
/// Create the module without checking the passed code.
///
/// # Note
@@ -350,7 +287,7 @@ impl<T: Config> WasmBlob<T> {
/// our results. This also does not collect any deposit from the `owner`. Also useful
/// during testing when we want to deploy codes that do not pass the instantiation checks.
#[cfg(any(test, feature = "runtime-benchmarks"))]
fn from_code_unchecked(
pub fn from_code_unchecked(
code: Vec<u8>,
schedule: &Schedule<T>,
owner: T::AccountId,
@@ -450,9 +387,8 @@ impl<T: Config> Executable<T> for WasmBlob<T> {
Error::<T>::CodeRejected
})?;
// We store before executing so that the code hash is available in the constructor.
if let &ExportedFunction::Constructor = function {
Self::store_code(self, true)?;
WasmBlob::<T>::increment_refcount(self.code_hash)?;
}
let result = exported_func.call(&mut store, &[], &mut []);
@@ -790,7 +726,6 @@ mod tests {
ext.borrow_mut().schedule(),
ALICE,
Determinism::Enforced,
TryInstantiate::Instantiate,
)
.map_err(|err| err.0)?
};