Skip to content

Commit 7aacdf1

Browse files
pgherveouagryaznov
andauthored
contracts: Refactor instantiate with code (paritytech#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>
1 parent 5584139 commit 7aacdf1

7 files changed

Lines changed: 780 additions & 809 deletions

File tree

frame/contracts/primitives/src/lib.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,6 @@ pub enum Code<Hash> {
161161
Existing(Hash),
162162
}
163163

164-
impl<T: Into<Vec<u8>>, Hash> From<T> for Code<Hash> {
165-
fn from(from: T) -> Self {
166-
Code::Upload(from.into())
167-
}
168-
}
169-
170164
/// The amount of balance that was either charged or refunded in order to pay for storage.
171165
#[derive(Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, Clone, TypeInfo)]
172166
pub enum StorageDeposit<Balance> {

frame/contracts/src/lib.rs

Lines changed: 114 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
//! allowed to code owner.
7676
//! * [`Pallet::set_code`] - Changes the code of an existing contract. Only allowed to `Root`
7777
//! origin.
78-
//! * [`Pallet::migrate`] - Runs migration steps of curent multi-block migration in priority, before
79-
//! [`Hooks::on_idle`][frame_support::traits::Hooks::on_idle] activates.
78+
//! * [`Pallet::migrate`] - Runs migration steps of current multi-block migration in priority,
79+
//! before [`Hooks::on_idle`][frame_support::traits::Hooks::on_idle] activates.
8080
//!
8181
//! ## Usage
8282
//!
@@ -105,7 +105,7 @@ use crate::{
105105
exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack},
106106
gas::GasMeter,
107107
storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
108-
wasm::{CodeInfo, TryInstantiate, WasmBlob},
108+
wasm::{CodeInfo, WasmBlob},
109109
};
110110
use codec::{Codec, Decode, Encode, HasCompact};
111111
use environmental::*;
@@ -695,24 +695,40 @@ pub mod pallet {
695695
salt: Vec<u8>,
696696
) -> DispatchResultWithPostInfo {
697697
Migration::<T>::ensure_migrated()?;
698+
let origin = ensure_signed(origin)?;
698699
let code_len = code.len() as u32;
700+
701+
let (module, upload_deposit) = Self::try_upload_code(
702+
origin.clone(),
703+
code,
704+
storage_deposit_limit.clone().map(Into::into),
705+
Determinism::Enforced,
706+
None,
707+
)?;
708+
709+
// Reduces the storage deposit limit by the amount that was reserved for the upload.
710+
let storage_deposit_limit =
711+
storage_deposit_limit.map(|limit| limit.into().saturating_sub(upload_deposit));
712+
699713
let data_len = data.len() as u32;
700714
let salt_len = salt.len() as u32;
701715
let common = CommonInput {
702-
origin: Origin::from_runtime_origin(origin)?,
716+
origin: Origin::from_account_id(origin),
703717
value,
704718
data,
705719
gas_limit,
706-
storage_deposit_limit: storage_deposit_limit.map(Into::into),
720+
storage_deposit_limit,
707721
debug_message: None,
708722
};
723+
709724
let mut output =
710-
InstantiateInput::<T> { code: Code::Upload(code), salt }.run_guarded(common);
725+
InstantiateInput::<T> { code: WasmCode::Wasm(module), salt }.run_guarded(common);
711726
if let Ok(retval) = &output.result {
712727
if retval.1.did_revert() {
713728
output.result = Err(<Error<T>>::ContractReverted.into());
714729
}
715730
}
731+
716732
output.gas_meter.into_dispatch_result(
717733
output.result.map(|(_address, result)| result),
718734
T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len),
@@ -748,8 +764,8 @@ pub mod pallet {
748764
storage_deposit_limit: storage_deposit_limit.map(Into::into),
749765
debug_message: None,
750766
};
751-
let mut output =
752-
InstantiateInput::<T> { code: Code::Existing(code_hash), salt }.run_guarded(common);
767+
let mut output = InstantiateInput::<T> { code: WasmCode::CodeHash(code_hash), salt }
768+
.run_guarded(common);
753769
if let Ok(retval) = &output.result {
754770
if retval.1.did_revert() {
755771
output.result = Err(<Error<T>>::ContractReverted.into());
@@ -1052,9 +1068,15 @@ struct CallInput<T: Config> {
10521068
determinism: Determinism,
10531069
}
10541070

1071+
/// Reference to an existing code hash or a new wasm module.
1072+
enum WasmCode<T: Config> {
1073+
Wasm(WasmBlob<T>),
1074+
CodeHash(CodeHash<T>),
1075+
}
1076+
10551077
/// Input specific to a contract instantiation invocation.
10561078
struct InstantiateInput<T: Config> {
1057-
code: Code<CodeHash<T>>,
1079+
code: WasmCode<T>,
10581080
salt: Vec<u8>,
10591081
}
10601082

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

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

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

@@ -1159,11 +1181,8 @@ trait Invokable<T: Config> {
11591181
/// contract or a instantiation of a new one.
11601182
///
11611183
/// Called by dispatchables and public functions through the [`Invokable::run_guarded`].
1162-
fn run(
1163-
&self,
1164-
common: CommonInput<T>,
1165-
gas_meter: GasMeter<T>,
1166-
) -> InternalOutput<T, Self::Output>;
1184+
fn run(self, common: CommonInput<T>, gas_meter: GasMeter<T>)
1185+
-> InternalOutput<T, Self::Output>;
11671186

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

11771196
fn run(
1178-
&self,
1197+
self,
11791198
common: CommonInput<T>,
11801199
mut gas_meter: GasMeter<T>,
11811200
) -> InternalOutput<T, Self::Output> {
@@ -1201,7 +1220,7 @@ impl<T: Config> Invokable<T> for CallInput<T> {
12011220
value,
12021221
data.clone(),
12031222
debug_message,
1204-
*determinism,
1223+
determinism,
12051224
);
12061225

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

12251244
fn run(
1226-
&self,
1227-
mut common: CommonInput<T>,
1245+
self,
1246+
common: CommonInput<T>,
12281247
mut gas_meter: GasMeter<T>,
12291248
) -> InternalOutput<T, Self::Output> {
12301249
let mut storage_deposit = Default::default();
@@ -1233,37 +1252,15 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
12331252
let InstantiateInput { salt, .. } = self;
12341253
let CommonInput { origin: contract_origin, .. } = common;
12351254
let origin = contract_origin.account_id()?;
1236-
let (extra_deposit, executable) = match &self.code {
1237-
Code::Upload(binary) => {
1238-
let executable = WasmBlob::from_code(
1239-
binary.clone(),
1240-
&schedule,
1241-
origin.clone(),
1242-
Determinism::Enforced,
1243-
TryInstantiate::Skip,
1244-
)
1245-
.map_err(|(err, msg)| {
1246-
common
1247-
.debug_message
1248-
.as_mut()
1249-
.map(|buffer| buffer.try_extend(&mut msg.bytes()));
1250-
err
1251-
})?;
1252-
// The open deposit will be charged during execution when the
1253-
// uploaded module does not already exist. This deposit is not part of the
1254-
// storage meter because it is not transferred to the contract but
1255-
// reserved on the uploading account.
1256-
(executable.open_deposit(&executable.code_info()), executable)
1257-
},
1258-
Code::Existing(hash) =>
1259-
(Default::default(), WasmBlob::from_storage(*hash, &mut gas_meter)?),
1255+
1256+
let executable = match self.code {
1257+
WasmCode::Wasm(module) => module,
1258+
WasmCode::CodeHash(code_hash) => WasmBlob::from_storage(code_hash, &mut gas_meter)?,
12601259
};
1260+
12611261
let contract_origin = Origin::from_account_id(origin.clone());
1262-
let mut storage_meter = StorageMeter::new(
1263-
&contract_origin,
1264-
common.storage_deposit_limit,
1265-
common.value.saturating_add(extra_deposit),
1266-
)?;
1262+
let mut storage_meter =
1263+
StorageMeter::new(&contract_origin, common.storage_deposit_limit, common.value)?;
12671264
let CommonInput { value, data, debug_message, .. } = common;
12681265
let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
12691266
origin.clone(),
@@ -1277,9 +1274,7 @@ impl<T: Config> Invokable<T> for InstantiateInput<T> {
12771274
debug_message,
12781275
);
12791276

1280-
storage_deposit = storage_meter
1281-
.try_into_deposit(&contract_origin)?
1282-
.saturating_add(&StorageDeposit::Charge(extra_deposit));
1277+
storage_deposit = storage_meter.try_into_deposit(&contract_origin)?;
12831278
result
12841279
};
12851280
InternalOutput { result: try_exec(), gas_meter, storage_deposit }
@@ -1383,7 +1378,7 @@ impl<T: Config> Pallet<T> {
13831378
origin: T::AccountId,
13841379
value: BalanceOf<T>,
13851380
gas_limit: Weight,
1386-
storage_deposit_limit: Option<BalanceOf<T>>,
1381+
mut storage_deposit_limit: Option<BalanceOf<T>>,
13871382
code: Code<CodeHash<T>>,
13881383
data: Vec<u8>,
13891384
salt: Vec<u8>,
@@ -1397,6 +1392,45 @@ impl<T: Config> Pallet<T> {
13971392
} else {
13981393
None
13991394
};
1395+
// collect events if CollectEvents is UnsafeCollect
1396+
let events = || {
1397+
if collect_events == CollectEvents::UnsafeCollect {
1398+
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
1399+
} else {
1400+
None
1401+
}
1402+
};
1403+
1404+
let (code, upload_deposit): (WasmCode<T>, BalanceOf<T>) = match code {
1405+
Code::Upload(code) => {
1406+
let result = Self::try_upload_code(
1407+
origin.clone(),
1408+
code,
1409+
storage_deposit_limit.map(Into::into),
1410+
Determinism::Enforced,
1411+
debug_message.as_mut(),
1412+
);
1413+
1414+
let (module, deposit) = match result {
1415+
Ok(result) => result,
1416+
Err(error) =>
1417+
return ContractResult {
1418+
gas_consumed: Zero::zero(),
1419+
gas_required: Zero::zero(),
1420+
storage_deposit: Default::default(),
1421+
debug_message: debug_message.unwrap_or(Default::default()).into(),
1422+
result: Err(error),
1423+
events: events(),
1424+
},
1425+
};
1426+
1427+
storage_deposit_limit =
1428+
storage_deposit_limit.map(|l| l.saturating_sub(deposit.into()));
1429+
(WasmCode::Wasm(module), deposit)
1430+
},
1431+
Code::Existing(hash) => (WasmCode::CodeHash(hash), Default::default()),
1432+
};
1433+
14001434
let common = CommonInput {
14011435
origin: Origin::from_account_id(origin),
14021436
value,
@@ -1405,23 +1439,20 @@ impl<T: Config> Pallet<T> {
14051439
storage_deposit_limit,
14061440
debug_message: debug_message.as_mut(),
14071441
};
1442+
14081443
let output = InstantiateInput::<T> { code, salt }.run_guarded(common);
1409-
// collect events if CollectEvents is UnsafeCollect
1410-
let events = if collect_events == CollectEvents::UnsafeCollect {
1411-
Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
1412-
} else {
1413-
None
1414-
};
14151444
ContractInstantiateResult {
14161445
result: output
14171446
.result
14181447
.map(|(account_id, result)| InstantiateReturnValue { result, account_id })
14191448
.map_err(|e| e.error),
14201449
gas_consumed: output.gas_meter.gas_consumed(),
14211450
gas_required: output.gas_meter.gas_required(),
1422-
storage_deposit: output.storage_deposit,
1451+
storage_deposit: output
1452+
.storage_deposit
1453+
.saturating_add(&StorageDeposit::Charge(upload_deposit)),
14231454
debug_message: debug_message.unwrap_or_default().to_vec(),
1424-
events,
1455+
events: events(),
14251456
}
14261457
}
14271458

@@ -1436,17 +1467,31 @@ impl<T: Config> Pallet<T> {
14361467
determinism: Determinism,
14371468
) -> CodeUploadResult<CodeHash<T>, BalanceOf<T>> {
14381469
Migration::<T>::ensure_migrated()?;
1470+
let (module, deposit) =
1471+
Self::try_upload_code(origin, code, storage_deposit_limit, determinism, None)?;
1472+
Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1473+
}
1474+
1475+
/// Uploads new code and returns the Wasm blob and deposit amount collected.
1476+
fn try_upload_code(
1477+
origin: T::AccountId,
1478+
code: Vec<u8>,
1479+
storage_deposit_limit: Option<BalanceOf<T>>,
1480+
determinism: Determinism,
1481+
mut debug_message: Option<&mut DebugBufferVec<T>>,
1482+
) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
14391483
let schedule = T::Schedule::get();
1440-
let module =
1441-
WasmBlob::from_code(code, &schedule, origin, determinism, TryInstantiate::Instantiate)
1442-
.map_err(|(err, _)| err)?;
1443-
let deposit = module.open_deposit(&module.code_info());
1484+
let mut module =
1485+
WasmBlob::from_code(code, &schedule, origin, determinism).map_err(|(err, msg)| {
1486+
debug_message.as_mut().map(|d| d.try_extend(msg.bytes()));
1487+
err
1488+
})?;
1489+
let deposit = module.store_code()?;
14441490
if let Some(storage_deposit_limit) = storage_deposit_limit {
14451491
ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
14461492
}
1447-
let result = CodeUploadReturnValue { code_hash: *module.code_hash(), deposit };
1448-
module.store()?;
1449-
Ok(result)
1493+
1494+
Ok((module, deposit))
14501495
}
14511496

14521497
/// Query storage of a specified contract under a specified key.
@@ -1490,7 +1535,7 @@ impl<T: Config> Pallet<T> {
14901535
owner: T::AccountId,
14911536
) -> frame_support::dispatch::DispatchResult {
14921537
let schedule = T::Schedule::get();
1493-
WasmBlob::store_code_unchecked(code, &schedule, owner)?;
1538+
WasmBlob::<T>::from_code_unchecked(code, &schedule, owner)?.store_code()?;
14941539
Ok(())
14951540
}
14961541

frame/contracts/src/migration/v11.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ impl<T: Config> MigrationStep for Migration<T> {
8080
}
8181

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

8688
log::debug!(

0 commit comments

Comments
 (0)