Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: Refactor instantiate with code #14503

Merged
merged 31 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
304d84b
wip
pgherveou Jul 3, 2023
4e80ea6
fixes
pgherveou Jul 3, 2023
dc744af
rm comment
pgherveou Jul 3, 2023
5f9a226
join fns
pgherveou Jul 3, 2023
b6def6d
clippy
pgherveou Jul 4, 2023
76c43c2
Fix limits
pgherveou Jul 4, 2023
83e5f37
reduce diff
pgherveou Jul 4, 2023
e3c51ec
fix
pgherveou Jul 4, 2023
8c15372
fix
pgherveou Jul 4, 2023
c97708b
fix typo
pgherveou Jul 4, 2023
0180777
refactor store to use self
pgherveou Jul 4, 2023
89accc8
refactor run to take self by value
pgherveou Jul 4, 2023
0e61306
pass tests
pgherveou Jul 4, 2023
49c174e
rm comment
pgherveou Jul 4, 2023
9f53e36
fixes
pgherveou Jul 5, 2023
5036e88
fix typo
pgherveou Jul 5, 2023
09a9971
rm
pgherveou Jul 5, 2023
6248a64
fix fmt
pgherveou Jul 5, 2023
e137f0e
clippy
pgherveou Jul 5, 2023
672e92e
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jul 5, 2023
68ba74d
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts
Jul 5, 2023
e8ceb0d
Update frame/contracts/src/lib.rs
pgherveou Jul 6, 2023
9eb7833
Update frame/contracts/src/wasm/mod.rs
pgherveou Jul 6, 2023
96e3dcf
Update frame/contracts/src/wasm/mod.rs
pgherveou Jul 6, 2023
a9df0cc
PR review, rm duplicate increment_refcount
pgherveou Jul 6, 2023
b291902
PR review
pgherveou Jul 6, 2023
bc2057d
Update frame/contracts/src/wasm/prepare.rs
pgherveou Jul 6, 2023
bbf2c5b
Add test for failing storage_deposit
pgherveou Jul 6, 2023
dff4758
fix lint
pgherveou Jul 6, 2023
e1b73af
Merge branch 'master' of https://github.com/paritytech/substrate into…
Jul 6, 2023
5837c09
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts
Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions frame/contracts/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,6 @@ pub enum Code<Hash> {
Existing(Hash),
}

impl<T: Into<Vec<u8>>, Hash> From<T> for Code<Hash> {
fn from(from: T) -> Self {
Code::Upload(from.into())
}
}

/// The amount of balance that was either charged or refunded in order to pay for storage.
#[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone, TypeInfo)]
pub enum StorageDeposit<Balance> {
Expand Down
183 changes: 114 additions & 69 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
//! allowed to code owner.
//! * [`Pallet::set_code`] - Changes the code of an existing contract. Only allowed to `Root`
//! origin.
//! * [`Pallet::migrate`] - Runs migration steps of curent multi-block migration in priority, before
//! [`Hooks::on_idle`][frame_support::traits::Hooks::on_idle] activates.
//! * [`Pallet::migrate`] - Runs migration steps of current multi-block migration in priority,
//! before [`Hooks::on_idle`][frame_support::traits::Hooks::on_idle] activates.
//!
//! ## Usage
//!
Expand Down Expand Up @@ -105,7 +105,7 @@ use crate::{
exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack},
gas::GasMeter,
storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
wasm::{CodeInfo, TryInstantiate, WasmBlob},
wasm::{CodeInfo, WasmBlob},
};
use codec::{Codec, Decode, Encode, HasCompact};
use environmental::*;
Expand Down Expand Up @@ -695,24 +695,40 @@ pub mod pallet {
salt: Vec<u8>,
) -> DispatchResultWithPostInfo {
Migration::<T>::ensure_migrated()?;
let origin = ensure_signed(origin)?;
let code_len = code.len() as u32;

let (module, upload_deposit) = Self::try_upload_code(
origin.clone(),
code,
storage_deposit_limit.clone().map(Into::into),
Determinism::Enforced,
None,
)?;

// Reduces the storage deposit limit by the amount that was reserved for the upload.
let storage_deposit_limit =
storage_deposit_limit.map(|limit| limit.into().saturating_sub(upload_deposit));

let data_len = data.len() as u32;
let salt_len = salt.len() as u32;
let common = CommonInput {
origin: Origin::from_runtime_origin(origin)?,
origin: Origin::from_account_id(origin),
value,
data,
gas_limit,
storage_deposit_limit: storage_deposit_limit.map(Into::into),
storage_deposit_limit,
debug_message: None,
};

let mut output =
InstantiateInput::<T> { code: Code::Upload(code), salt }.run_guarded(common);
InstantiateInput::<T> { code: WasmCode::Wasm(module), salt }.run_guarded(common);
if let Ok(retval) = &output.result {
if retval.1.did_revert() {
output.result = Err(<Error<T>>::ContractReverted.into());
}
}

output.gas_meter.into_dispatch_result(
output.result.map(|(_address, result)| result),
T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len),
Expand Down Expand Up @@ -748,8 +764,8 @@ pub mod pallet {
storage_deposit_limit: storage_deposit_limit.map(Into::into),
debug_message: None,
};
let mut output =
InstantiateInput::<T> { code: Code::Existing(code_hash), salt }.run_guarded(common);
let mut output = InstantiateInput::<T> { code: WasmCode::CodeHash(code_hash), salt }
.run_guarded(common);
if let Ok(retval) = &output.result {
if retval.1.did_revert() {
output.result = Err(<Error<T>>::ContractReverted.into());
Expand Down Expand Up @@ -1052,9 +1068,15 @@ struct CallInput<T: Config> {
determinism: Determinism,
}

/// Reference to an existing code hash or a new wasm module.
enum WasmCode<T: Config> {
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
Wasm(WasmBlob<T>),
CodeHash(CodeHash<T>),
}

/// Input specific to a contract instantiation invocation.
struct InstantiateInput<T: Config> {
code: Code<CodeHash<T>>,
code: WasmCode<T>,
salt: Vec<u8>,
}

Expand Down Expand Up @@ -1099,7 +1121,7 @@ struct InternalOutput<T: Config, O> {

/// Helper trait to wrap contract execution entry points into a single function
/// [`Invokable::run_guarded`].
trait Invokable<T: Config> {
trait Invokable<T: Config>: Sized {
/// What is returned as a result of a successful invocation.
type Output;

Expand All @@ -1111,7 +1133,7 @@ trait Invokable<T: Config> {
///
/// We enforce a re-entrancy guard here by initializing and checking a boolean flag through a
/// global reference.
fn run_guarded(&self, common: CommonInput<T>) -> InternalOutput<T, Self::Output> {
fn run_guarded(self, common: CommonInput<T>) -> InternalOutput<T, Self::Output> {
// Set up a global reference to the boolean flag used for the re-entrancy guard.
environmental!(executing_contract: bool);

Expand Down Expand Up @@ -1159,11 +1181,8 @@ trait Invokable<T: Config> {
/// contract or a instantiation of a new one.
///
/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
fn run(
&self,
common: CommonInput<T>,
gas_meter: GasMeter<T>,
) -> InternalOutput<T, Self::Output>;
fn run(self, common: CommonInput<T>, gas_meter: GasMeter<T>)
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
-> InternalOutput<T, Self::Output>;

/// This method ensures that the given `origin` is allowed to invoke the current `Invokable`.
///
Expand All @@ -1175,7 +1194,7 @@ impl<T: Config> Invokable<T> for CallInput<T> {
type Output = ExecReturnValue;

fn run(
&self,
self,
common: CommonInput<T>,
mut gas_meter: GasMeter<T>,
) -> InternalOutput<T, Self::Output> {
Expand All @@ -1201,7 +1220,7 @@ impl<T: Config> Invokable<T> for CallInput<T> {
value,
data.clone(),
debug_message,
*determinism,
determinism,
);

match storage_meter.try_into_deposit(&origin) {
Expand All @@ -1223,8 +1242,8 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
type Output = (AccountIdOf<T>, ExecReturnValue);

fn run(
&self,
mut common: CommonInput<T>,
self,
common: CommonInput<T>,
mut gas_meter: GasMeter<T>,
) -> InternalOutput<T, Self::Output> {
let mut storage_deposit = Default::default();
Expand All @@ -1233,37 +1252,15 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
let InstantiateInput { salt, .. } = self;
let CommonInput { origin: contract_origin, .. } = common;
let origin = contract_origin.account_id()?;
let (extra_deposit, executable) = match &self.code {
Code::Upload(binary) => {
let executable = WasmBlob::from_code(
binary.clone(),
&schedule,
origin.clone(),
Determinism::Enforced,
TryInstantiate::Skip,
)
.map_err(|(err, msg)| {
common
.debug_message
.as_mut()
.map(|buffer| buffer.try_extend(&mut msg.bytes()));
err
})?;
// The open deposit will be charged during execution when the
// uploaded module does not already exist. This deposit is not part of the
// storage meter because it is not transferred to the contract but
// reserved on the uploading account.
(executable.open_deposit(&executable.code_info()), executable)
},
Code::Existing(hash) =>
(Default::default(), WasmBlob::from_storage(*hash, &mut gas_meter)?),

let executable = match self.code {
WasmCode::Wasm(module) => module,
WasmCode::CodeHash(code_hash) => WasmBlob::from_storage(code_hash, &mut gas_meter)?,
};

let contract_origin = Origin::from_account_id(origin.clone());
let mut storage_meter = StorageMeter::new(
&contract_origin,
common.storage_deposit_limit,
common.value.saturating_add(extra_deposit),
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
)?;
let mut storage_meter =
StorageMeter::new(&contract_origin, common.storage_deposit_limit, common.value)?;
agryaznov marked this conversation as resolved.
Show resolved Hide resolved
let CommonInput { value, data, debug_message, .. } = common;
let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
origin.clone(),
Expand All @@ -1277,9 +1274,7 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
debug_message,
);

storage_deposit = storage_meter
.try_into_deposit(&contract_origin)?
.saturating_add(&StorageDeposit::Charge(extra_deposit));
storage_deposit = storage_meter.try_into_deposit(&contract_origin)?;
result
};
InternalOutput { result: try_exec(), gas_meter, storage_deposit }
Expand Down Expand Up @@ -1383,7 +1378,7 @@ impl<T: Config> Pallet<T> {
origin: T::AccountId,
value: BalanceOf<T>,
gas_limit: Weight,
storage_deposit_limit: Option<BalanceOf<T>>,
mut storage_deposit_limit: Option<BalanceOf<T>>,
code: Code<CodeHash<T>>,
data: Vec<u8>,
salt: Vec<u8>,
Expand All @@ -1397,6 +1392,45 @@ impl<T: Config> Pallet<T> {
} else {
None
};
// collect events if CollectEvents is UnsafeCollect
let events = || {
if collect_events == CollectEvents::UnsafeCollect {
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
} else {
None
}
};

let (code, upload_deposit): (WasmCode<T>, BalanceOf<T>) = match code {
Code::Upload(code) => {
let result = Self::try_upload_code(
origin.clone(),
code,
storage_deposit_limit.map(Into::into),
Determinism::Enforced,
debug_message.as_mut(),
);

let (module, deposit) = match result {
Ok(result) => result,
Err(error) =>
return ContractResult {
gas_consumed: Zero::zero(),
gas_required: Zero::zero(),
storage_deposit: Default::default(),
debug_message: debug_message.unwrap_or(Default::default()).into(),
result: Err(error),
events: events(),
},
};

storage_deposit_limit =
storage_deposit_limit.map(|l| l.saturating_sub(deposit.into()));
(WasmCode::Wasm(module), deposit)
},
Code::Existing(hash) => (WasmCode::CodeHash(hash), Default::default()),
};

let common = CommonInput {
origin: Origin::from_account_id(origin),
value,
Expand All @@ -1405,23 +1439,20 @@ impl<T: Config> Pallet<T> {
storage_deposit_limit,
debug_message: debug_message.as_mut(),
};

let output = InstantiateInput::<T> { code, salt }.run_guarded(common);
// collect events if CollectEvents is UnsafeCollect
let events = if collect_events == CollectEvents::UnsafeCollect {
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
} else {
None
};
ContractInstantiateResult {
result: output
.result
.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
.map_err(|e| e.error),
gas_consumed: output.gas_meter.gas_consumed(),
gas_required: output.gas_meter.gas_required(),
storage_deposit: output.storage_deposit,
storage_deposit: output
.storage_deposit
.saturating_add(&StorageDeposit::Charge(upload_deposit)),
debug_message: debug_message.unwrap_or_default().to_vec(),
events,
events: events(),
}
}

Expand All @@ -1436,17 +1467,31 @@ impl<T: Config> Pallet<T> {
determinism: Determinism,
) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
Migration::<T>::ensure_migrated()?;
let (module, deposit) =
Self::try_upload_code(origin, code, storage_deposit_limit, determinism, None)?;
Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
}

/// Uploads new code and returns the Wasm blob and deposit amount collected.
fn try_upload_code(
origin: T::AccountId,
code: Vec<u8>,
storage_deposit_limit: Option<BalanceOf<T>>,
determinism: Determinism,
mut debug_message: Option<&mut DebugBufferVec<T>>,
) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
let schedule = T::Schedule::get();
let module =
WasmBlob::from_code(code, &schedule, origin, determinism, TryInstantiate::Instantiate)
.map_err(|(err, _)| err)?;
let deposit = module.open_deposit(&module.code_info());
let mut module =
WasmBlob::from_code(code, &schedule, origin, determinism).map_err(|(err, msg)| {
debug_message.as_mut().map(|d| d.try_extend(msg.bytes()));
err
})?;
let deposit = module.store_code()?;
if let Some(storage_deposit_limit) = storage_deposit_limit {
ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
}
let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit };
module.store()?;
Ok(result)

Ok((module, deposit))
}

/// Query storage of a specified contract under a specified key.
Expand Down Expand Up @@ -1490,7 +1535,7 @@ impl<T: Config> Pallet<T> {
owner: T::AccountId,
) -> frame_support::dispatch::DispatchResult {
let schedule = T::Schedule::get();
WasmBlob::store_code_unchecked(code, &schedule, owner)?;
WasmBlob::<T>::from_code_unchecked(code, &schedule, owner)?.store_code()?;
Ok(())
}

Expand Down
4 changes: 3 additions & 1 deletion frame/contracts/src/migration/v11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ impl<T: Config> MigrationStep for Migration<T> {
}

fn step(&mut self) -> (IsFinished, Weight) {
let Some(old_queue) = old::DeletionQueue::<T>::take() else { return (IsFinished::Yes, Weight::zero()) };
let Some(old_queue) = old::DeletionQueue::<T>::take() else {
return (IsFinished::Yes, Weight::zero())
};
let len = old_queue.len();

log::debug!(
Expand Down
Loading