Skip to main content

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