1pub 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, traits::tokens::Restriction},
30 gas::{GasMeter, Token},
31 storage::meter::Diff,
32 weights::WeightInfo,
33 AccountIdOf, BalanceOf, CodeInfoOf, CodeRemoved, Config, Error, ExecError, HoldReason,
34 PristineCode, Weight, LOG_TARGET,
35};
36use alloc::vec::Vec;
37use codec::{Decode, Encode, MaxEncodedLen};
38use frame_support::{
39 dispatch::DispatchResult,
40 traits::{
41 fungible::MutateHold,
42 tokens::{Fortitude, Precision, Preservation},
43 },
44};
45use pallet_revive_uapi::ReturnErrorCode;
46use sp_core::{Get, H256, U256};
47use sp_runtime::DispatchError;
48
49#[derive(Encode, Decode, scale_info::TypeInfo)]
52#[codec(mel_bound())]
53#[scale_info(skip_type_params(T))]
54pub struct ContractBlob<T: Config> {
55 code: Vec<u8>,
56 #[codec(skip)]
58 code_info: CodeInfo<T>,
59 #[codec(skip)]
61 code_hash: H256,
62}
63
64#[derive(
65 PartialEq, Eq, Debug, Copy, Clone, Encode, Decode, MaxEncodedLen, scale_info::TypeInfo,
66)]
67pub enum BytecodeType {
68 Pvm,
70 Evm,
72}
73
74#[derive(
82 frame_support::DebugNoBound, Clone, Encode, Decode, scale_info::TypeInfo, MaxEncodedLen,
83)]
84#[codec(mel_bound())]
85#[scale_info(skip_type_params(T))]
86pub struct CodeInfo<T: Config> {
87 owner: AccountIdOf<T>,
89 #[codec(compact)]
91 deposit: BalanceOf<T>,
92 #[codec(compact)]
94 refcount: u64,
95 code_len: u32,
97 code_type: BytecodeType,
99 behaviour_version: u32,
107}
108
109pub fn calculate_code_deposit<T: Config>(code_len: u32) -> BalanceOf<T> {
111 let bytes_added = code_len.saturating_add(<CodeInfo<T>>::max_encoded_len() as u32);
112 Diff { bytes_added, items_added: 2, ..Default::default() }
113 .update_contract::<T>(None)
114 .charge_or_zero()
115}
116
117impl ExportedFunction {
118 fn identifier(&self) -> &str {
120 match self {
121 Self::Constructor => "deploy",
122 Self::Call => "call",
123 }
124 }
125}
126
127#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
129#[derive(Clone, Copy)]
130struct CodeLoadToken {
131 code_len: u32,
132 code_type: BytecodeType,
133}
134
135impl CodeLoadToken {
136 fn from_code_info<T: Config>(code_info: &CodeInfo<T>) -> Self {
137 Self { code_len: code_info.code_len, code_type: code_info.code_type }
138 }
139}
140
141impl<T: Config> Token<T> for CodeLoadToken {
142 fn weight(&self) -> Weight {
143 match self.code_type {
144 BytecodeType::Pvm => T::WeightInfo::call_with_pvm_code_per_byte(self.code_len)
148 .saturating_sub(T::WeightInfo::call_with_pvm_code_per_byte(0))
149 .saturating_add(
150 T::WeightInfo::basic_block_compilation(1)
151 .saturating_sub(T::WeightInfo::basic_block_compilation(0))
152 .set_proof_size(0),
153 ),
154 BytecodeType::Evm => T::WeightInfo::call_with_evm_code_per_byte(self.code_len)
155 .saturating_sub(T::WeightInfo::call_with_evm_code_per_byte(0)),
156 }
157 }
158}
159
160#[cfg(test)]
161pub fn code_load_weight(code_len: u32) -> Weight {
162 Token::<crate::tests::Test>::weight(&CodeLoadToken { code_len, code_type: BytecodeType::Pvm })
163}
164
165impl<T: Config> ContractBlob<T>
166where
167 BalanceOf<T>: Into<U256> + TryFrom<U256>,
168{
169 pub fn remove(origin: &T::AccountId, code_hash: H256) -> DispatchResult {
173 <CodeInfoOf<T>>::try_mutate_exists(&code_hash, |existing| {
174 if let Some(code_info) = existing {
175 ensure!(code_info.refcount == 0, <Error<T>>::CodeInUse);
176 ensure!(&code_info.owner == origin, BadOrigin);
177 T::Currency::transfer_on_hold(
178 &HoldReason::CodeUploadDepositReserve.into(),
179 &crate::Pallet::<T>::account_id(),
180 &code_info.owner,
181 code_info.deposit,
182 Precision::Exact,
183 Restriction::Free,
184 Fortitude::Polite,
185 )?;
186
187 *existing = None;
188 <PristineCode<T>>::remove(&code_hash);
189 Ok(())
190 } else {
191 Err(<Error<T>>::CodeNotFound.into())
192 }
193 })
194 }
195
196 pub fn store_code(&mut self, skip_transfer: bool) -> Result<BalanceOf<T>, Error<T>> {
198 let code_hash = *self.code_hash();
199 ensure!(code_hash != H256::zero(), <Error<T>>::CodeNotFound);
200
201 <CodeInfoOf<T>>::mutate(code_hash, |stored_code_info| {
202 match stored_code_info {
203 Some(_) => Ok(Default::default()),
205 None => {
210 let deposit = self.code_info.deposit;
211
212 if !skip_transfer {
213 T::Currency::transfer_and_hold(
214 &HoldReason::CodeUploadDepositReserve.into(),
215 &self.code_info.owner,
216 &crate::Pallet::<T>::account_id(),
217 deposit,
218 Precision::Exact,
219 Preservation::Preserve,
220 Fortitude::Polite,
221 )
222 .map_err(|err| {
223 log::debug!(target: LOG_TARGET, "failed to hold store code deposit {deposit:?} for owner: {:?}: {err:?}", self.code_info.owner);
224 <Error<T>>::StorageDepositNotEnoughFunds
225 })?;
226 }
227
228 <PristineCode<T>>::insert(code_hash, &self.code.to_vec());
229 *stored_code_info = Some(self.code_info.clone());
230 Ok(deposit)
231 },
232 }
233 })
234 }
235}
236
237impl<T: Config> CodeInfo<T> {
238 #[cfg(test)]
239 pub fn new(owner: T::AccountId) -> Self {
240 CodeInfo {
241 owner,
242 deposit: Default::default(),
243 refcount: 0,
244 code_len: 0,
245 code_type: BytecodeType::Pvm,
246 behaviour_version: Default::default(),
247 }
248 }
249
250 #[cfg(test)]
252 pub fn refcount(&self) -> u64 {
253 self.refcount
254 }
255
256 pub fn deposit(&self) -> BalanceOf<T> {
258 self.deposit
259 }
260
261 pub fn code_len(&self) -> u64 {
263 self.code_len.into()
264 }
265
266 pub fn is_pvm(&self) -> bool {
268 matches!(self.code_type, BytecodeType::Pvm)
269 }
270
271 pub fn increment_refcount(code_hash: H256) -> DispatchResult {
279 <CodeInfoOf<T>>::mutate(code_hash, |existing| -> Result<(), DispatchError> {
280 if let Some(info) = existing {
281 info.refcount = info
282 .refcount
283 .checked_add(1)
284 .ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
285 Ok(())
286 } else {
287 Err(Error::<T>::CodeNotFound.into())
288 }
289 })
290 }
291
292 pub fn decrement_refcount(code_hash: H256) -> Result<CodeRemoved, DispatchError> {
295 <CodeInfoOf<T>>::try_mutate_exists(code_hash, |existing| {
296 let Some(code_info) = existing else { return Err(Error::<T>::CodeNotFound.into()) };
297
298 if code_info.refcount == 1 {
299 T::Currency::transfer_on_hold(
300 &HoldReason::CodeUploadDepositReserve.into(),
301 &crate::Pallet::<T>::account_id(),
302 &code_info.owner,
303 code_info.deposit,
304 Precision::Exact,
305 Restriction::Free,
306 Fortitude::Polite,
307 )?;
308
309 *existing = None;
310 <PristineCode<T>>::remove(&code_hash);
311
312 Ok(CodeRemoved::Yes)
313 } else {
314 code_info.refcount = code_info
315 .refcount
316 .checked_sub(1)
317 .ok_or_else(|| <Error<T>>::RefcountOverOrUnderflow)?;
318 Ok(CodeRemoved::No)
319 }
320 })
321 }
322}
323
324impl<T: Config> Executable<T> for ContractBlob<T>
325where
326 BalanceOf<T>: Into<U256> + TryFrom<U256>,
327{
328 fn from_storage(code_hash: H256, gas_meter: &mut GasMeter<T>) -> Result<Self, DispatchError> {
329 let code_info = <CodeInfoOf<T>>::get(code_hash).ok_or(Error::<T>::CodeNotFound)?;
330 gas_meter.charge(CodeLoadToken::from_code_info(&code_info))?;
331 let code = <PristineCode<T>>::get(&code_hash).ok_or(Error::<T>::CodeNotFound)?;
332 Ok(Self { code, code_info, code_hash })
333 }
334
335 fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
336 ContractBlob::from_evm_init_code(code, owner)
337 }
338
339 fn execute<E: Ext<T = T>>(
340 self,
341 ext: &mut E,
342 function: ExportedFunction,
343 input_data: Vec<u8>,
344 ) -> ExecResult {
345 if self.code_info().is_pvm() {
346 let prepared_call =
347 self.prepare_call(pvm::Runtime::new(ext, input_data), function, 0)?;
348 prepared_call.call()
349 } else if T::AllowEVMBytecode::get() {
350 use crate::vm::evm::EVMInputs;
351 use revm::bytecode::Bytecode;
352 let inputs = EVMInputs::new(input_data);
353 let bytecode = Bytecode::new_raw(self.code.into());
354 evm::call(bytecode, ext, inputs)
355 } else {
356 Err(Error::<T>::CodeRejected.into())
357 }
358 }
359
360 fn code(&self) -> &[u8] {
361 self.code.as_ref()
362 }
363
364 fn code_hash(&self) -> &H256 {
365 &self.code_hash
366 }
367
368 fn code_info(&self) -> &CodeInfo<T> {
369 &self.code_info
370 }
371}
372
373pub(crate) fn exec_error_into_return_code<E: Ext>(
378 from: ExecError,
379) -> Result<ReturnErrorCode, DispatchError> {
380 use crate::exec::ErrorOrigin::Callee;
381 use ReturnErrorCode::*;
382
383 let transfer_failed = Error::<E::T>::TransferFailed.into();
384 let out_of_gas = Error::<E::T>::OutOfGas.into();
385 let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
386 let duplicate_contract = Error::<E::T>::DuplicateContract.into();
387 let unsupported_precompile = Error::<E::T>::UnsupportedPrecompileAddress.into();
388
389 match (from.error, from.origin) {
391 (err, _) if err == transfer_failed => Ok(TransferFailed),
392 (err, _) if err == duplicate_contract => Ok(DuplicateContractAddress),
393 (err, _) if err == unsupported_precompile => Err(err),
394 (err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources),
395 (_, Callee) => Ok(CalleeTrapped),
396 (err, _) => Err(err),
397 }
398}