pallet_revive/vm/
mod.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//! This module provides a means for executing contracts
19//! represented in vm bytecode.
20
21pub mod evm;
22pub mod pvm;
23mod runtime_costs;
24
25pub use runtime_costs::RuntimeCosts;
26
27use crate::{
28	exec::{ExecResult, Executable, ExportedFunction, Ext},
29	frame_support::{ensure, error::BadOrigin},
30	metering::{ResourceMeter, State, Token},
31	weights::WeightInfo,
32	AccountIdOf, BalanceOf, CodeInfoOf, CodeRemoved, Config, Error, ExecConfig, ExecError,
33	HoldReason, Pallet, PristineCode, StorageDeposit, Weight, LOG_TARGET,
34};
35use alloc::vec::Vec;
36use codec::{Decode, Encode, MaxEncodedLen};
37use frame_support::dispatch::DispatchResult;
38use pallet_revive_uapi::ReturnErrorCode;
39use sp_core::{Get, H256};
40use sp_runtime::{DispatchError, Saturating};
41
42/// Validated Vm module ready for execution.
43/// This data structure is immutable once created and stored.
44#[derive(Encode, Decode, scale_info::TypeInfo)]
45#[codec(mel_bound())]
46#[scale_info(skip_type_params(T))]
47pub struct ContractBlob<T: Config> {
48	code: Vec<u8>,
49	// This isn't needed for contract execution and is not stored alongside it.
50	#[codec(skip)]
51	code_info: CodeInfo<T>,
52	// This is for not calculating the hash every time we need it.
53	#[codec(skip)]
54	code_hash: H256,
55}
56
57#[derive(
58	PartialEq, Eq, Debug, Copy, Clone, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo,
59)]
60pub enum BytecodeType {
61	/// The code is a PVM bytecode.
62	Pvm,
63	/// The code is an EVM bytecode.
64	Evm,
65}
66
67/// Contract code related data, such as:
68///
69/// - owner of the contract, i.e. account uploaded its code,
70/// - storage deposit amount,
71/// - reference count,
72///
73/// It is stored in a separate storage entry to avoid loading the code when not necessary.
74#[derive(
75	frame_support::DebugNoBound, Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen,
76)]
77#[codec(mel_bound())]
78#[scale_info(skip_type_params(T))]
79pub struct CodeInfo<T: Config> {
80	/// The account that has uploaded the contract code and hence is allowed to remove it.
81	owner: AccountIdOf<T>,
82	/// The amount of balance that was deposited by the owner in order to store it on-chain.
83	#[codec(compact)]
84	deposit: BalanceOf<T>,
85	/// The number of instantiated contracts that use this as their code.
86	#[codec(compact)]
87	refcount: u64,
88	/// Length of the code in bytes.
89	code_len: u32,
90	/// Bytecode type
91	code_type: BytecodeType,
92	/// The behaviour version that this contract operates under.
93	///
94	/// Whenever any observeable change (with the exception of weights) are made we need
95	/// to make sure that already deployed contracts will not be affected. We do this by
96	/// exposing the old behaviour depending on the set behaviour version of the contract.
97	///
98	/// As of right now this is a reserved field that is always set to 0.
99	behaviour_version: u32,
100}
101
102/// Calculate the deposit required for storing code and its metadata.
103pub fn calculate_code_deposit<T: Config>(code_len: u32) -> BalanceOf<T> {
104	let bytes_added = code_len.saturating_add(<CodeInfo<T>>::max_encoded_len() as u32);
105	T::DepositPerByte::get()
106		.saturating_mul(bytes_added.into())
107		.saturating_add(T::DepositPerItem::get().saturating_mul(2u32.into()))
108}
109
110impl ExportedFunction {
111	/// The vm export name for the function.
112	fn identifier(&self) -> &str {
113		match self {
114			Self::Constructor => "deploy",
115			Self::Call => "call",
116		}
117	}
118}
119
120/// Cost of code loading from storage.
121#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
122#[derive(Clone, Copy)]
123struct CodeLoadToken {
124	code_len: u32,
125	code_type: BytecodeType,
126}
127
128impl CodeLoadToken {
129	fn from_code_info<T: Config>(code_info: &CodeInfo<T>) -> Self {
130		Self { code_len: code_info.code_len, code_type: code_info.code_type }
131	}
132}
133
134impl<T: Config> Token<T> for CodeLoadToken {
135	fn weight(&self) -> Weight {
136		match self.code_type {
137			// the proof size impact is accounted for in the `call_with_pvm_code_per_byte`
138			// strictly speaking we are double charging for the first BASIC_BLOCK_SIZE
139			// instructions here. Let's consider this as a safety margin.
140			BytecodeType::Pvm => T::WeightInfo::call_with_pvm_code_per_byte(self.code_len)
141				.saturating_sub(T::WeightInfo::call_with_pvm_code_per_byte(0))
142				.saturating_add(
143					T::WeightInfo::basic_block_compilation(1)
144						.saturating_sub(T::WeightInfo::basic_block_compilation(0))
145						.set_proof_size(0),
146				),
147			BytecodeType::Evm => T::WeightInfo::call_with_evm_code_per_byte(self.code_len)
148				.saturating_sub(T::WeightInfo::call_with_evm_code_per_byte(0)),
149		}
150	}
151}
152
153#[cfg(test)]
154pub fn code_load_weight(code_len: u32) -> Weight {
155	Token::<crate::tests::Test>::weight(&CodeLoadToken { code_len, code_type: BytecodeType::Pvm })
156}
157
158impl<T: Config> ContractBlob<T> {
159	/// Remove the code from storage and refund the deposit to its owner.
160	///
161	/// Applies all necessary checks before removing the code.
162	pub fn remove(origin: &T::AccountId, code_hash: H256) -> DispatchResult {
163		<CodeInfoOf<T>>::try_mutate_exists(&code_hash, |existing| {
164			if let Some(code_info) = existing {
165				ensure!(code_info.refcount == 0, <Error<T>>::CodeInUse);
166				ensure!(&code_info.owner == origin, BadOrigin);
167				<Pallet<T>>::refund_deposit(
168					HoldReason::CodeUploadDepositReserve,
169					&Pallet::<T>::account_id(),
170					&code_info.owner,
171					code_info.deposit,
172					None,
173				)?;
174				*existing = None;
175				<PristineCode<T>>::remove(&code_hash);
176				Ok(())
177			} else {
178				Err(<Error<T>>::CodeNotFound.into())
179			}
180		})
181	}
182
183	/// Puts the module blob into storage, and returns the deposit collected for the storage.
184	pub fn store_code<S: State>(
185		&mut self,
186		exec_config: &ExecConfig<T>,
187		meter: &mut ResourceMeter<T, S>,
188	) -> Result<BalanceOf<T>, DispatchError> {
189		let code_hash = *self.code_hash();
190		ensure!(code_hash != H256::zero(), <Error<T>>::CodeNotFound);
191
192		<CodeInfoOf<T>>::mutate(code_hash, |stored_code_info| {
193			match stored_code_info {
194				// Contract code is already stored in storage. Nothing to be done here.
195				Some(_) => Ok(Default::default()),
196				// Upload a new contract code.
197				// We need to store the code and its code_info, and collect the deposit.
198				// This `None` case happens only with freshly uploaded modules. This means that
199				// the `owner` is always the origin of the current transaction.
200				None => {
201					let deposit = self.code_info.deposit;
202
203					<Pallet<T>>::charge_deposit(
204							Some(HoldReason::CodeUploadDepositReserve),
205							&self.code_info.owner,
206							&Pallet::<T>::account_id(),
207							deposit,
208							exec_config,
209						)
210					 .inspect_err(|err| {
211							log::debug!(target: LOG_TARGET, "failed to hold store code deposit {deposit:?} for owner: {:?}: {err:?}", self.code_info.owner);
212					})?;
213
214					meter.charge_deposit(&StorageDeposit::Charge(deposit))?;
215
216					<PristineCode<T>>::insert(code_hash, &self.code.to_vec());
217					*stored_code_info = Some(self.code_info.clone());
218					Ok(deposit)
219				},
220			}
221		})
222	}
223}
224
225impl<T: Config> CodeInfo<T> {
226	#[cfg(test)]
227	pub fn new(owner: T::AccountId) -> Self {
228		CodeInfo {
229			owner,
230			deposit: Default::default(),
231			refcount: 0,
232			code_len: 0,
233			code_type: BytecodeType::Pvm,
234			behaviour_version: Default::default(),
235		}
236	}
237
238	/// Returns reference count of the module.
239	#[cfg(test)]
240	pub fn refcount(&self) -> u64 {
241		self.refcount
242	}
243
244	/// Returns the deposit of the module.
245	pub fn deposit(&self) -> BalanceOf<T> {
246		self.deposit
247	}
248
249	/// Returns the code length.
250	pub fn code_len(&self) -> u64 {
251		self.code_len.into()
252	}
253
254	/// Returns true if the executable is a PVM blob.
255	pub fn is_pvm(&self) -> bool {
256		matches!(self.code_type, BytecodeType::Pvm)
257	}
258
259	/// Returns the number of times the specified contract exists on the call stack. Delegated calls
260	/// Increment the reference count of a stored code by one.
261	///
262	/// # Errors
263	///
264	/// [`Error::CodeNotFound`] is returned if no stored code found having the specified
265	/// `code_hash`.
266	pub fn increment_refcount(code_hash: H256) -> DispatchResult {
267		<CodeInfoOf<T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
268			if let Some(info) = existing {
269				info.refcount = info
270					.refcount
271					.checked_add(1)
272					.ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
273				Ok(())
274			} else {
275				Err(Error::<T>::CodeNotFound.into())
276			}
277		})
278	}
279
280	/// Decrement the reference count of a stored code by one.
281	/// Remove the code from storage when the reference count is zero.
282	pub fn decrement_refcount(code_hash: H256) -> Result<CodeRemoved, DispatchError> {
283		<CodeInfoOf<T>>::try_mutate_exists(code_hash, |existing| {
284			let Some(code_info) = existing else { return Err(Error::<T>::CodeNotFound.into()) };
285
286			if code_info.refcount == 1 {
287				<Pallet<T>>::refund_deposit(
288					HoldReason::CodeUploadDepositReserve,
289					&Pallet::<T>::account_id(),
290					&code_info.owner,
291					code_info.deposit,
292					None,
293				)?;
294
295				*existing = None;
296				<PristineCode<T>>::remove(&code_hash);
297
298				Ok(CodeRemoved::Yes)
299			} else {
300				code_info.refcount = code_info
301					.refcount
302					.checked_sub(1)
303					.ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
304				Ok(CodeRemoved::No)
305			}
306		})
307	}
308}
309
310impl<T: Config> Executable<T> for ContractBlob<T> {
311	fn from_storage<S: State>(
312		code_hash: H256,
313		meter: &mut ResourceMeter<T, S>,
314	) -> Result<Self, DispatchError> {
315		let code_info = <CodeInfoOf<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
316		meter.charge_weight_token(CodeLoadToken::from_code_info(&code_info))?;
317		let code = <PristineCode<T>>::get(&code_hash).ok_or(Error::<T>::CodeNotFound)?;
318		Ok(Self { code, code_info, code_hash })
319	}
320
321	fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
322		ContractBlob::from_evm_init_code(code, owner)
323	}
324
325	fn execute<E: Ext<T = T>>(
326		self,
327		ext: &mut E,
328		function: ExportedFunction,
329		input_data: Vec<u8>,
330	) -> ExecResult {
331		if self.code_info().is_pvm() {
332			let prepared_call =
333				self.prepare_call(pvm::Runtime::new(ext, input_data), function, 0)?;
334			prepared_call.call()
335		} else if T::AllowEVMBytecode::get() {
336			use revm::bytecode::Bytecode;
337			let bytecode = Bytecode::new_raw(self.code.into());
338			evm::call(bytecode, ext, input_data)
339		} else {
340			Err(Error::<T>::CodeRejected.into())
341		}
342	}
343
344	fn code(&self) -> &[u8] {
345		self.code.as_ref()
346	}
347
348	fn code_hash(&self) -> &H256 {
349		&self.code_hash
350	}
351
352	fn code_info(&self) -> &CodeInfo<T> {
353		&self.code_info
354	}
355}
356
357/// Fallible conversion of a `ExecError` to `ReturnErrorCode`.
358///
359/// This is used when converting the error returned from a subcall in order to decide
360/// whether to trap the caller or allow handling of the error.
361pub(crate) fn exec_error_into_return_code<E: Ext>(
362	from: ExecError,
363) -> Result<ReturnErrorCode, DispatchError> {
364	use crate::exec::ErrorOrigin::Callee;
365	use ReturnErrorCode::*;
366
367	let transfer_failed = Error::<E::T>::TransferFailed.into();
368	let out_of_gas = Error::<E::T>::OutOfGas.into();
369	let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
370	let duplicate_contract = Error::<E::T>::DuplicateContract.into();
371	let unsupported_precompile = Error::<E::T>::UnsupportedPrecompileAddress.into();
372
373	// errors in the callee do not trap the caller
374	match (from.error, from.origin) {
375		(err, _) if err == transfer_failed => Ok(TransferFailed),
376		(err, _) if err == duplicate_contract => Ok(DuplicateContractAddress),
377		(err, _) if err == unsupported_precompile => Err(err),
378		(err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources),
379		(_, Callee) => Ok(CalleeTrapped),
380		(err, _) => Err(err),
381	}
382}