pallet_revive/
primitives.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! A crate that hosts a common definitions that are relevant for the pallet-revive.
19
20use crate::{
21	evm::DryRunConfig, mock::MockHandler, storage::WriteOutcome, BalanceOf, Config, Time, H160,
22	U256,
23};
24use alloc::{boxed::Box, fmt::Debug, string::String, vec::Vec};
25use codec::{Decode, Encode, MaxEncodedLen};
26use frame_support::{traits::tokens::Balance, weights::Weight};
27use pallet_revive_uapi::ReturnFlags;
28use scale_info::TypeInfo;
29use sp_core::Get;
30use sp_runtime::{
31	traits::{One, Saturating, Zero},
32	DispatchError, RuntimeDebug,
33};
34
35/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and
36/// `ContractsApi::instantiate`.
37///
38/// It contains the execution result together with some auxiliary information.
39///
40/// #Note
41///
42/// It has been extended to include `events` at the end of the struct while not bumping the
43/// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data
44/// should be ignored to avoid any potential compatibility issues.
45#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
46pub struct ContractResult<R, Balance> {
47	/// How much weight was consumed during execution.
48	pub weight_consumed: Weight,
49	/// How much weight is required as weight limit in order to execute this call.
50	///
51	/// This value should be used to determine the weight limit for on-chain execution.
52	///
53	/// # Note
54	///
55	/// This can only be different from [`Self::weight_consumed`] when weight pre charging
56	/// is used. Currently, only `seal_call_runtime` makes use of pre charging.
57	/// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging
58	/// when a non-zero `weight_limit` argument is supplied.
59	pub weight_required: Weight,
60	/// How much balance was paid by the origin into the contract's deposit account in order to
61	/// pay for storage.
62	///
63	/// The storage deposit is never actually charged from the origin in case of [`Self::result`]
64	/// is `Err`. This is because on error all storage changes are rolled back including the
65	/// payment of the deposit.
66	pub storage_deposit: StorageDeposit<Balance>,
67	/// The maximal storage deposit amount that occured at any time during the execution.
68	/// This can be higher than the final storage_deposit due to refunds
69	/// This is always a StorageDeposit::Charge(..)
70	pub max_storage_deposit: StorageDeposit<Balance>,
71	/// The amount of Ethereum gas that has been consumed during execution.
72	pub gas_consumed: Balance,
73	/// The execution result of the vm binary code.
74	pub result: Result<R, DispatchError>,
75}
76
77impl<R: Default, B: Balance> Default for ContractResult<R, B> {
78	fn default() -> Self {
79		Self {
80			weight_consumed: Default::default(),
81			weight_required: Default::default(),
82			storage_deposit: Default::default(),
83			max_storage_deposit: Default::default(),
84			gas_consumed: Default::default(),
85			result: Ok(Default::default()),
86		}
87	}
88}
89
90/// The result of the execution of a `eth_transact` call.
91#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo)]
92pub struct EthTransactInfo<Balance> {
93	/// The amount of weight that was necessary to execute the transaction.
94	pub weight_required: Weight,
95	/// Final storage deposit charged.
96	pub storage_deposit: Balance,
97	/// Maximal storage deposit charged at any time during execution.
98	pub max_storage_deposit: Balance,
99	/// The weight and deposit equivalent in EVM Gas.
100	pub eth_gas: U256,
101	/// The execution return value.
102	pub data: Vec<u8>,
103}
104
105/// Error type of a `eth_transact` call.
106#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
107pub enum EthTransactError {
108	Data(Vec<u8>),
109	Message(String),
110}
111
112#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
113/// Error encountered while creating a BalanceWithDust from a U256 balance.
114pub enum BalanceConversionError {
115	/// Error encountered while creating the main balance value.
116	Value,
117	/// Error encountered while creating the dust value.
118	Dust,
119}
120
121/// A Balance amount along with some "dust" to represent the lowest decimals that can't be expressed
122/// in the native currency
123#[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
124pub struct BalanceWithDust<Balance> {
125	/// The value expressed in the native currency
126	value: Balance,
127	/// The dust, representing up to 1 unit of the native currency.
128	/// The dust is bounded between 0 and `crate::Config::NativeToEthRatio`
129	dust: u32,
130}
131
132impl<Balance> From<Balance> for BalanceWithDust<Balance> {
133	fn from(value: Balance) -> Self {
134		Self { value, dust: 0 }
135	}
136}
137
138impl<Balance> BalanceWithDust<Balance> {
139	/// Deconstructs the `BalanceWithDust` into its components.
140	pub fn deconstruct(self) -> (Balance, u32) {
141		(self.value, self.dust)
142	}
143
144	/// Creates a new `BalanceWithDust` with the given value and dust.
145	pub fn new_unchecked<T: Config>(value: Balance, dust: u32) -> Self {
146		debug_assert!(dust < T::NativeToEthRatio::get());
147		Self { value, dust }
148	}
149
150	/// Creates a new `BalanceWithDust` from the given EVM value.
151	pub fn from_value<T: Config>(
152		value: U256,
153	) -> Result<BalanceWithDust<BalanceOf<T>>, BalanceConversionError> {
154		if value.is_zero() {
155			return Ok(Default::default())
156		}
157
158		let (quotient, remainder) = value.div_mod(T::NativeToEthRatio::get().into());
159		let value = quotient.try_into().map_err(|_| BalanceConversionError::Value)?;
160		let dust = remainder.try_into().map_err(|_| BalanceConversionError::Dust)?;
161
162		Ok(BalanceWithDust { value, dust })
163	}
164}
165
166impl<Balance: Zero + One + Saturating> BalanceWithDust<Balance> {
167	/// Returns true if both the value and dust are zero.
168	pub fn is_zero(&self) -> bool {
169		self.value.is_zero() && self.dust == 0
170	}
171
172	/// Returns the Balance rounded to the nearest whole unit if the dust is non-zero.
173	pub fn into_rounded_balance(self) -> Balance {
174		if self.dust == 0 {
175			self.value
176		} else {
177			self.value.saturating_add(Balance::one())
178		}
179	}
180}
181
182/// Result type of a `bare_code_upload` call.
183pub type CodeUploadResult<Balance> = Result<CodeUploadReturnValue<Balance>, DispatchError>;
184
185/// Result type of a `get_storage` call.
186pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
187
188/// Result type of a `set_storage` call.
189pub type SetStorageResult = Result<WriteOutcome, ContractAccessError>;
190
191/// The possible errors that can happen querying the storage of a contract.
192#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
193pub enum ContractAccessError {
194	/// The given address doesn't point to a contract.
195	DoesntExist,
196	/// Storage key cannot be decoded from the provided input data.
197	KeyDecodingFailed,
198	/// Writing to storage failed.
199	StorageWriteFailed(DispatchError),
200}
201
202/// Output of a contract call or instantiation which ran to completion.
203#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
204pub struct ExecReturnValue {
205	/// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
206	pub flags: ReturnFlags,
207	/// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
208	pub data: Vec<u8>,
209}
210
211impl ExecReturnValue {
212	/// The contract did revert all storage changes.
213	pub fn did_revert(&self) -> bool {
214		self.flags.contains(ReturnFlags::REVERT)
215	}
216}
217
218/// The result of a successful contract instantiation.
219#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
220pub struct InstantiateReturnValue {
221	/// The output of the called constructor.
222	pub result: ExecReturnValue,
223	/// The address of the new contract.
224	pub addr: H160,
225}
226
227/// The result of successfully uploading a contract.
228#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
229pub struct CodeUploadReturnValue<Balance> {
230	/// The key under which the new code is stored.
231	pub code_hash: sp_core::H256,
232	/// The deposit that was reserved at the caller. Is zero when the code already existed.
233	pub deposit: Balance,
234}
235
236/// Reference to an existing code hash or a new vm module.
237#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
238pub enum Code {
239	/// A vm module as raw bytes.
240	Upload(Vec<u8>),
241	/// The code hash of an on-chain vm binary blob.
242	Existing(sp_core::H256),
243}
244
245/// The amount of balance that was either charged or refunded in order to pay for storage.
246#[derive(
247	Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo,
248)]
249pub enum StorageDeposit<Balance> {
250	/// The transaction reduced storage consumption.
251	///
252	/// This means that the specified amount of balance was transferred from the involved
253	/// deposit accounts to the origin.
254	Refund(Balance),
255	/// The transaction increased storage consumption.
256	///
257	/// This means that the specified amount of balance was transferred from the origin
258	/// to the involved deposit accounts.
259	Charge(Balance),
260}
261
262impl<T, Balance> ContractResult<T, Balance> {
263	pub fn map_result<V>(self, map_fn: impl FnOnce(T) -> V) -> ContractResult<V, Balance> {
264		ContractResult {
265			weight_consumed: self.weight_consumed,
266			weight_required: self.weight_required,
267			storage_deposit: self.storage_deposit,
268			max_storage_deposit: self.max_storage_deposit,
269			gas_consumed: self.gas_consumed,
270			result: self.result.map(map_fn),
271		}
272	}
273}
274
275impl<Balance: Zero> Default for StorageDeposit<Balance> {
276	fn default() -> Self {
277		Self::Charge(Zero::zero())
278	}
279}
280
281impl<Balance: Zero + Copy> StorageDeposit<Balance> {
282	/// Returns how much balance is charged or `0` in case of a refund.
283	pub fn charge_or_zero(&self) -> Balance {
284		match self {
285			Self::Charge(amount) => *amount,
286			Self::Refund(_) => Zero::zero(),
287		}
288	}
289
290	pub fn is_zero(&self) -> bool {
291		match self {
292			Self::Charge(amount) => amount.is_zero(),
293			Self::Refund(amount) => amount.is_zero(),
294		}
295	}
296}
297
298impl<Balance> StorageDeposit<Balance>
299where
300	Balance: frame_support::traits::tokens::Balance + Saturating + Ord + Copy,
301{
302	/// This is essentially a saturating signed add.
303	pub fn saturating_add(&self, rhs: &Self) -> Self {
304		use StorageDeposit::*;
305		match (self, rhs) {
306			(Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)),
307			(Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)),
308			(Charge(lhs), Refund(rhs)) =>
309				if lhs >= rhs {
310					Charge(lhs.saturating_sub(*rhs))
311				} else {
312					Refund(rhs.saturating_sub(*lhs))
313				},
314			(Refund(lhs), Charge(rhs)) =>
315				if lhs > rhs {
316					Refund(lhs.saturating_sub(*rhs))
317				} else {
318					Charge(rhs.saturating_sub(*lhs))
319				},
320		}
321	}
322
323	/// This is essentially a saturating signed sub.
324	pub fn saturating_sub(&self, rhs: &Self) -> Self {
325		use StorageDeposit::*;
326		match (self, rhs) {
327			(Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)),
328			(Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)),
329			(Charge(lhs), Charge(rhs)) =>
330				if lhs >= rhs {
331					Charge(lhs.saturating_sub(*rhs))
332				} else {
333					Refund(rhs.saturating_sub(*lhs))
334				},
335			(Refund(lhs), Refund(rhs)) =>
336				if lhs > rhs {
337					Refund(lhs.saturating_sub(*rhs))
338				} else {
339					Charge(rhs.saturating_sub(*lhs))
340				},
341		}
342	}
343
344	/// If the amount of deposit (this type) is constrained by a `limit` this calculates how
345	/// much balance (if any) is still available from this limit.
346	///
347	/// # Note
348	///
349	/// In case of a refund the return value can be larger than `limit`.
350	pub fn available(&self, limit: &Balance) -> Option<Balance> {
351		use StorageDeposit::*;
352		match self {
353			Charge(amount) => limit.checked_sub(amount),
354			Refund(amount) => Some(limit.saturating_add(*amount)),
355		}
356	}
357}
358
359/// `Stack` wide configuration options.
360pub struct ExecConfig<T: Config> {
361	/// Indicates whether the account nonce should be incremented after instantiating a new
362	/// contract.
363	///
364	/// In Substrate, where transactions can be batched, the account's nonce should be incremented
365	/// after each instantiation, ensuring that each instantiation uses a unique nonce.
366	///
367	/// For transactions sent from Ethereum wallets, which cannot be batched, the nonce should only
368	/// be incremented once. In these cases, set this to `false` to suppress an extra nonce
369	/// increment.
370	///
371	/// Note:
372	/// The origin's nonce is already incremented pre-dispatch by the `CheckNonce` transaction
373	/// extension.
374	///
375	/// This does not apply to contract initiated instantatiations. Those will always bump the
376	/// instantiating contract's nonce.
377	pub bump_nonce: bool,
378	/// Whether deposits will be withdrawn from the pallet_transaction_payment credit (`Some`)
379	/// free balance (`None`).
380	///
381	/// Contains the encoded_len + base weight.
382	pub collect_deposit_from_hold: Option<(u32, Weight)>,
383	/// The gas price that was chosen for this transaction.
384	///
385	/// It is determined when transforming `eth_transact` into a proper extrinsic.
386	pub effective_gas_price: Option<U256>,
387	/// Whether this configuration was created for a dry-run execution.
388	/// Use to enable logic that should only run in dry-run mode.
389	pub is_dry_run: Option<DryRunConfig<<<T as Config>::Time as Time>::Moment>>,
390	/// An optional mock handler that can be used to override certain behaviors.
391	/// This is primarily used for testing purposes and should be `None` in production
392	/// environments.
393	pub mock_handler: Option<Box<dyn MockHandler<T>>>,
394}
395
396impl<T: Config> ExecConfig<T> {
397	/// Create a default config appropriate when the call originated from a substrate tx.
398	pub fn new_substrate_tx() -> Self {
399		Self {
400			bump_nonce: true,
401			collect_deposit_from_hold: None,
402			effective_gas_price: None,
403			is_dry_run: None,
404			mock_handler: None,
405		}
406	}
407
408	pub fn new_substrate_tx_without_bump() -> Self {
409		Self {
410			bump_nonce: false,
411			collect_deposit_from_hold: None,
412			effective_gas_price: None,
413			mock_handler: None,
414			is_dry_run: None,
415		}
416	}
417
418	/// Create a default config appropriate when the call originated from a ethereum tx.
419	pub fn new_eth_tx(effective_gas_price: U256, encoded_len: u32, base_weight: Weight) -> Self {
420		Self {
421			bump_nonce: false,
422			collect_deposit_from_hold: Some((encoded_len, base_weight)),
423			effective_gas_price: Some(effective_gas_price),
424			mock_handler: None,
425			is_dry_run: None,
426		}
427	}
428
429	/// Set this config to be a dry-run.
430	pub fn with_dry_run(
431		mut self,
432		dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
433	) -> Self {
434		self.is_dry_run = Some(dry_run_config);
435		self
436	}
437
438	/// Almost clone for testing (does not clone mock_handler)
439	#[cfg(test)]
440	pub fn clone(&self) -> Self {
441		Self {
442			bump_nonce: self.bump_nonce,
443			collect_deposit_from_hold: self.collect_deposit_from_hold,
444			effective_gas_price: self.effective_gas_price,
445			is_dry_run: self.is_dry_run.clone(),
446			mock_handler: None,
447		}
448	}
449}
450
451/// Indicates whether the code was removed after the last refcount was decremented.
452#[must_use = "You must handle whether the code was removed or not."]
453pub enum CodeRemoved {
454	/// The code was not removed. (refcount > 0)
455	No,
456	/// The code was removed. (refcount == 0)
457	Yes,
458}