pallet_evm_polkavm/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
22#![warn(unused_crate_dependencies)]
23
24extern crate alloc;
25
26pub mod vm;
27mod weights;
28
29use core::marker::PhantomData;
30use fp_evm::{
31 ExitError, ExitRevert, ExitSucceed, IsPrecompileResult, PrecompileFailure, PrecompileHandle,
32 PrecompileOutput, PrecompileSet,
33};
34use sp_core::{H160, H256};
35
36pub use self::{pallet::*, weights::WeightInfo};
37
38pub trait CreateAddressScheme<AccountId> {
39 fn create_address_scheme(caller: AccountId, code: &[u8], salt: H256) -> H160;
40}
41
42pub trait ConvertPolkaVmGas {
43 fn polkavm_gas_to_evm_gas(gas: polkavm::Gas) -> u64;
44 fn evm_gas_to_polkavm_gas(gas: u64) -> polkavm::Gas;
45}
46
47pub struct PolkaVmSet<Inner, T>(pub Inner, PhantomData<T>);
48
49impl<Inner, T> PolkaVmSet<Inner, T> {
50 pub fn new(inner: Inner) -> Self {
51 Self(inner, PhantomData)
52 }
53}
54
55impl<Inner: PrecompileSet, T: Config> PrecompileSet for PolkaVmSet<Inner, T> {
56 fn execute(
57 &self,
58 handle: &mut impl PrecompileHandle,
59 ) -> Option<Result<PrecompileOutput, PrecompileFailure>> {
60 let code_address = handle.code_address();
61 let code = pallet_evm::AccountCodes::<T>::get(code_address);
62 if code[0..8] == vm::PREFIX {
63 let mut run = || {
64 let prepared_call: vm::PreparedCall<'_, T, _> = vm::PreparedCall::load(handle)?;
65 prepared_call.call()
66 };
67
68 match run() {
69 Ok(val) => {
70 if val.did_revert() {
71 Some(Err(PrecompileFailure::Revert {
72 exit_status: ExitRevert::Reverted,
73 output: val.data,
74 }))
75 } else {
76 Some(Ok(PrecompileOutput {
77 exit_status: ExitSucceed::Returned,
78 output: val.data,
79 }))
80 }
81 }
82 Err(_) => Some(Err(PrecompileFailure::Error {
83 exit_status: ExitError::Other("polkavm failure".into()),
84 })),
85 }
86 } else {
87 self.0.execute(handle)
88 }
89 }
90
91 fn is_precompile(&self, address: H160, remaining_gas: u64) -> IsPrecompileResult {
92 let code = pallet_evm::AccountCodes::<T>::get(address);
93 if code[0..8] == vm::PREFIX {
94 IsPrecompileResult::Answer {
95 is_precompile: true,
96 extra_cost: 0,
97 }
98 } else {
99 self.0.is_precompile(address, remaining_gas)
100 }
101 }
102}
103
104#[frame_support::pallet]
105pub mod pallet {
106 use super::{ConvertPolkaVmGas, CreateAddressScheme, WeightInfo};
107 use fp_evm::AccountProvider;
108 use frame_support::pallet_prelude::*;
109 use frame_system::pallet_prelude::*;
110 use pallet_evm::{
111 AccountCodes, AccountCodesMetadata, AddressMapping, CodeMetadata, Config as EConfig,
112 };
113 use sp_core::H256;
114
115 const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
116
117 #[pallet::pallet]
118 #[pallet::storage_version(STORAGE_VERSION)]
119 pub struct Pallet<T>(PhantomData<T>);
120
121 #[pallet::config]
122 pub trait Config: frame_system::Config + pallet_evm::Config {
123 type CreateAddressScheme: CreateAddressScheme<<Self as frame_system::Config>::AccountId>;
124 type ConvertPolkaVmGas: ConvertPolkaVmGas;
125 type MaxCodeSize: Get<u32>;
126 type WeightInfo: WeightInfo;
127 }
128
129 #[pallet::error]
130 pub enum Error<T> {
131 MaxCodeSizeExceeded,
133 NotPolkaVmContract,
135 AlreadyExist,
137 }
138
139 #[pallet::call]
140 impl<T: Config> Pallet<T> {
141 #[pallet::call_index(0)]
147 #[pallet::weight(<T as Config>::WeightInfo::create_polkavm(code.len() as u32))]
148 pub fn create_polkavm(origin: OriginFor<T>, code: Vec<u8>, salt: H256) -> DispatchResult {
149 if code.len() as u32 >= <T as Config>::MaxCodeSize::get() {
150 return Err(Error::<T>::MaxCodeSizeExceeded.into());
151 }
152
153 if code[0..8] != crate::vm::PREFIX {
154 return Err(Error::<T>::NotPolkaVmContract.into());
155 }
156
157 let caller = ensure_signed(origin)?;
158 let address =
159 <T as Config>::CreateAddressScheme::create_address_scheme(caller, &code[..], salt);
160
161 if <AccountCodes<T>>::contains_key(address) {
162 return Err(Error::<T>::AlreadyExist.into());
163 }
164
165 let account_id = <T as EConfig>::AddressMapping::into_account_id(address);
166 <T as EConfig>::AccountProvider::create_account(&account_id);
167
168 let meta = CodeMetadata::from_code(&code);
169 <AccountCodesMetadata<T>>::insert(address, meta);
170 <AccountCodes<T>>::insert(address, code);
171
172 Ok(())
173 }
174 }
175}