1pub mod evm;
22pub mod pvm;
23mod runtime_costs;
24
25pub use runtime_costs::RuntimeCosts;
26
27use crate::{
28 AccountIdOf, BalanceOf, CodeInfoOf, CodeRemoved, Config, Error, ExecConfig, ExecError,
29 HoldReason, LOG_TARGET, Pallet, PristineCode, StorageDeposit, Weight, deposit_payment,
30 exec::{ExecResult, Executable, ExportedFunction, Ext},
31 frame_support::{ensure, error::BadOrigin},
32 metering::{ResourceMeter, State, Token},
33 weights::WeightInfo,
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#[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 #[codec(skip)]
51 code_info: CodeInfo<T>,
52 #[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 Pvm,
63 Evm,
65}
66
67#[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 owner: AccountIdOf<T>,
82 #[codec(compact)]
84 deposit: BalanceOf<T>,
85 #[codec(compact)]
87 refcount: u64,
88 code_len: u32,
90 code_type: BytecodeType,
92 behaviour_version: u32,
100}
101
102pub 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 fn identifier(&self) -> &str {
113 match self {
114 Self::Constructor => "deploy",
115 Self::Call => "call",
116 }
117 }
118}
119
120#[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 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 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 deposit_payment::Funds::Balance(&code_info.owner),
171 code_info.deposit,
172 )?;
173 *existing = None;
174 <PristineCode<T>>::remove(&code_hash);
175 Ok(())
176 } else {
177 Err(<Error<T>>::CodeNotFound.into())
178 }
179 })
180 }
181
182 pub fn store_code<S: State>(
184 &mut self,
185 exec_config: &ExecConfig<T>,
186 meter: &mut ResourceMeter<T, S>,
187 ) -> Result<BalanceOf<T>, DispatchError> {
188 let code_hash = *self.code_hash();
189 ensure!(code_hash != H256::zero(), <Error<T>>::CodeNotFound);
190
191 <CodeInfoOf<T>>::mutate(code_hash, |stored_code_info| {
192 match stored_code_info {
193 Some(_) => Ok(Default::default()),
195 None => {
200 let deposit = self.code_info.deposit;
201
202 <Pallet<T>>::charge_deposit(
203 HoldReason::CodeUploadDepositReserve,
204 &self.code_info.owner,
205 &Pallet::<T>::account_id(),
206 deposit,
207 exec_config,
208 )
209 .inspect_err(|err| {
210 log::debug!(target: LOG_TARGET, "failed to hold store code deposit {deposit:?} for owner: {:?}: {err:?}", self.code_info.owner);
211 })?;
212
213 meter.charge_deposit(&StorageDeposit::Charge(deposit))?;
214
215 <PristineCode<T>>::insert(code_hash, &self.code.to_vec());
216 *stored_code_info = Some(self.code_info.clone());
217 Ok(deposit)
218 },
219 }
220 })
221 }
222}
223
224impl<T: Config> CodeInfo<T> {
225 #[cfg(test)]
226 pub fn new(owner: T::AccountId) -> Self {
227 CodeInfo {
228 owner,
229 deposit: Default::default(),
230 refcount: 0,
231 code_len: 0,
232 code_type: BytecodeType::Pvm,
233 behaviour_version: Default::default(),
234 }
235 }
236
237 #[cfg(any(feature = "runtime-benchmarks", test))]
238 pub fn new_with_deposit(owner: T::AccountId, deposit: BalanceOf<T>) -> Self {
239 CodeInfo {
240 owner,
241 deposit,
242 refcount: 0,
243 code_len: 0,
244 code_type: BytecodeType::Pvm,
245 behaviour_version: Default::default(),
246 }
247 }
248
249 #[cfg(test)]
251 pub fn refcount(&self) -> u64 {
252 self.refcount
253 }
254
255 pub fn deposit(&self) -> BalanceOf<T> {
257 self.deposit
258 }
259
260 pub fn owner(&self) -> &AccountIdOf<T> {
262 &self.owner
263 }
264
265 pub fn code_len(&self) -> u64 {
267 self.code_len.into()
268 }
269
270 pub fn is_pvm(&self) -> bool {
272 matches!(self.code_type, BytecodeType::Pvm)
273 }
274
275 pub fn increment_refcount(code_hash: H256) -> DispatchResult {
283 <CodeInfoOf<T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
284 if let Some(info) = existing {
285 info.refcount = info
286 .refcount
287 .checked_add(1)
288 .ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
289 Ok(())
290 } else {
291 Err(Error::<T>::CodeNotFound.into())
292 }
293 })
294 }
295
296 pub fn decrement_refcount(code_hash: H256) -> Result<CodeRemoved, DispatchError> {
299 <CodeInfoOf<T>>::try_mutate_exists(code_hash, |existing| {
300 let Some(code_info) = existing else { return Err(Error::<T>::CodeNotFound.into()) };
301
302 if code_info.refcount == 1 {
303 <Pallet<T>>::refund_deposit(
304 HoldReason::CodeUploadDepositReserve,
305 &Pallet::<T>::account_id(),
306 deposit_payment::Funds::Balance(&code_info.owner),
307 code_info.deposit,
308 )?;
309
310 *existing = None;
311 <PristineCode<T>>::remove(&code_hash);
312
313 Ok(CodeRemoved::Yes)
314 } else {
315 code_info.refcount = code_info
316 .refcount
317 .checked_sub(1)
318 .ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
319 Ok(CodeRemoved::No)
320 }
321 })
322 }
323}
324
325impl<T: Config> Executable<T> for ContractBlob<T> {
326 fn from_storage<S: State>(
327 code_hash: H256,
328 meter: &mut ResourceMeter<T, S>,
329 ) -> Result<Self, DispatchError> {
330 let code_info = <CodeInfoOf<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
331 meter.charge_weight_token(CodeLoadToken::from_code_info(&code_info))?;
332 let code = <PristineCode<T>>::get(&code_hash).ok_or(Error::<T>::CodeNotFound)?;
333 Ok(Self { code, code_info, code_hash })
334 }
335
336 fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
337 ContractBlob::from_evm_init_code(code, owner)
338 }
339
340 fn execute<E: Ext<T = T>>(
341 self,
342 ext: &mut E,
343 function: ExportedFunction,
344 input_data: Vec<u8>,
345 ) -> ExecResult {
346 if self.code_info().is_pvm() {
347 let prepared_call =
348 self.prepare_call(pvm::Runtime::new(ext, input_data), function, 0)?;
349 prepared_call.call()
350 } else if T::AllowEVMBytecode::get() {
351 use revm::bytecode::Bytecode;
352 let bytecode = Bytecode::new_raw(self.code.into());
353 evm::call(bytecode, ext, input_data)
354 } else {
355 Err(Error::<T>::CodeRejected.into())
356 }
357 }
358
359 fn code(&self) -> &[u8] {
360 self.code.as_ref()
361 }
362
363 fn code_hash(&self) -> &H256 {
364 &self.code_hash
365 }
366
367 fn code_info(&self) -> &CodeInfo<T> {
368 &self.code_info
369 }
370}
371
372pub(crate) fn exec_error_into_return_code<E: Ext>(
377 from: ExecError,
378) -> Result<ReturnErrorCode, DispatchError> {
379 use crate::exec::ErrorOrigin::Callee;
380 use ReturnErrorCode::*;
381
382 let transfer_failed = Error::<E::T>::TransferFailed.into();
383 let out_of_gas = Error::<E::T>::OutOfGas.into();
384 let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
385 let duplicate_contract = Error::<E::T>::DuplicateContract.into();
386 let unsupported_precompile = Error::<E::T>::UnsupportedPrecompileAddress.into();
387
388 match (from.error, from.origin) {
390 (err, _) if err == transfer_failed => Ok(TransferFailed),
391 (err, _) if err == duplicate_contract => Ok(DuplicateContractAddress),
392 (err, _) if err == unsupported_precompile => Err(err),
393 (err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources),
394 (_, Callee) => Ok(CalleeTrapped),
395 (err, _) => Err(err),
396 }
397}