1234567891011121314151617#![cfg_attr(not(feature = "std"), no_std)]1819extern crate self as pallet_evm_coder_substrate;2021#[cfg(not(feature = "std"))]22extern crate alloc;23#[cfg(not(feature = "std"))]24use alloc::format;25use core::marker::PhantomData;2627use execution::PreDispatch;28use frame_support::{29 ensure, pallet_prelude::DispatchError, sp_runtime::ModuleError, traits::PalletInfo,30};31use pallet_evm::{32 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileHandle,33 PrecompileOutput, PrecompileResult,34};35use parity_scale_codec::Decode;36use sp_core::{Get, H160};37use sp_std::{cell::RefCell, vec::Vec};38use sp_weights::Weight;39use up_data_structs::budget;404142pub mod execution;4344pub use evm_coder::{abi, solidity_interface, types, Contract, ResultWithPostInfoOf, ToLog};45use evm_coder::{46 types::{Msg, Value},47 AbiEncode,48};49pub use pallet::*;50#[doc(hidden)]51pub use spez::spez;5253#[frame_support::pallet]54pub mod pallet {55 pub use frame_support::dispatch::DispatchResult;5657 use super::*;5859 60 61 62 63 64 #[pallet::error]65 pub enum Error<T> {66 OutOfGas,67 OutOfFund,68 }6970 #[pallet::config]71 pub trait Config: frame_system::Config + pallet_evm::Config {}7273 #[pallet::pallet]74 pub struct Pallet<T>(_);75}767778pub const G_SLOAD_WORD: u64 = 800;79pub const G_SSTORE_WORD: u64 = 20000;8081pub struct GasCallsBudget<'r, T: Config> {82 recorder: &'r SubstrateRecorder<T>,83 gas_per_call: u64,84}85impl<T: Config> budget::Budget for GasCallsBudget<'_, T> {86 fn consume_custom(&self, calls: u32) -> bool {87 let (gas, overflown) = (calls as u64).overflowing_mul(self.gas_per_call);88 if overflown {89 return false;90 }91 self.recorder.consume_gas(gas).is_ok()92 }93}9495#[derive(Default)]96pub struct SubstrateRecorder<T: Config> {97 initial_gas: u64,98 gas_limit: RefCell<u64>,99 _phantom: PhantomData<*const T>,100}101102impl<T: Config> SubstrateRecorder<T> {103 pub fn new(gas_limit: u64) -> Self {104 Self {105 initial_gas: gas_limit,106 gas_limit: RefCell::new(gas_limit),107 _phantom: PhantomData,108 }109 }110111 pub fn gas_left(&self) -> u64 {112 *self.gas_limit.borrow()113 }114 pub fn gas_calls_budget(&self, gas_per_call: u64) -> GasCallsBudget<T> {115 GasCallsBudget {116 recorder: self,117 gas_per_call,118 }119 }120 pub fn weight_calls_budget(&self, weight_per_call: Weight) -> GasCallsBudget<T> {121 GasCallsBudget {122 recorder: self,123 gas_per_call: T::GasWeightMapping::weight_to_gas(weight_per_call),124 }125 }126 pub fn consume_sload_sub(&self) -> DispatchResult {127 self.consume_gas_sub(G_SLOAD_WORD)128 }129 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {130 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))131 }132 pub fn consume_sstore_sub(&self) -> DispatchResult {133 self.consume_gas_sub(G_SSTORE_WORD)134 }135 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {136 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);137 let mut gas_limit = self.gas_limit.borrow_mut();138 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);139 *gas_limit -= gas;140 Ok(())141 }142143 pub fn consume_sload(&self) -> execution::Result<()> {144 self.consume_gas(G_SLOAD_WORD)145 }146 pub fn consume_sstore(&self) -> execution::Result<()> {147 self.consume_gas(G_SSTORE_WORD)148 }149 pub fn consume_gas(&self, gas: u64) -> execution::Result<()> {150 if gas == u64::MAX {151 return Err(execution::Error::Error(ExitError::OutOfGas));152 }153 let mut gas_limit = self.gas_limit.borrow_mut();154 if gas > *gas_limit {155 return Err(execution::Error::Error(ExitError::OutOfGas));156 }157 *gas_limit -= gas;158 Ok(())159 }160 pub fn return_gas(&self, gas: u64) {161 let mut gas_limit = self.gas_limit.borrow_mut();162 *gas_limit += gas;163 }164165 pub fn evm_to_precompile_output(166 self,167 handle: &mut impl PrecompileHandle,168 result: execution::Result<Option<Vec<u8>>>,169 ) -> Option<PrecompileResult> {170 use execution::Error;171 172 let _ = handle.record_cost(self.initial_gas - self.gas_left());173 Some(match result {174 Ok(Some(v)) => Ok(PrecompileOutput {175 exit_status: ExitSucceed::Returned,176 output: v,177 }),178 Ok(None) => return None,179 Err(Error::Revert(e)) => Err(PrecompileFailure::Revert {180 exit_status: ExitRevert::Reverted,181 output: (&e as &str,).abi_encode_call(evm_coder::fn_selector!(Error(string))),182 }),183 Err(Error::Fatal(f)) => Err(PrecompileFailure::Fatal { exit_status: f }),184 Err(Error::Error(e)) => Err(e.into()),185 })186 }187188 189 pub fn consume_store_reads(&self, reads: u64) -> execution::Result<()> {190 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(191 <T as frame_system::Config>::DbWeight::get()192 .read193 .saturating_mul(reads),194 195 0,196 )))197 }198199 200 pub fn consume_store_writes(&self, writes: u64) -> execution::Result<()> {201 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(202 <T as frame_system::Config>::DbWeight::get()203 .write204 .saturating_mul(writes),205 206 0,207 )))208 }209210 211 pub fn consume_store_reads_and_writes(&self, reads: u64, writes: u64) -> execution::Result<()> {212 let weight = <T as frame_system::Config>::DbWeight::get();213 let reads = weight.read.saturating_mul(reads);214 let writes = weight.read.saturating_mul(writes);215 self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(216 reads.saturating_add(writes),217 218 0,219 )))220 }221}222223pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> execution::Error {224 use execution::Error as ExError;225 match err {226 DispatchError::Module(ModuleError { index, error, .. })227 if index228 == T::PalletInfo::index::<Pallet<T>>()229 .expect("evm-coder-substrate is a pallet, which should be added to runtime")230 as u8 =>231 {232 let mut read = &error as &[u8];233 match Error::<T>::decode(&mut read) {234 Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),235 Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),236 _ => unreachable!("this pallet only defines two possible errors"),237 }238 }239 DispatchError::Module(ModuleError {240 message: Some(msg), ..241 }) => ExError::Revert(msg.into()),242 DispatchError::Module(ModuleError { index, error, .. }) => {243 ExError::Revert(format!("error {error:?} in pallet {index}"))244 }245 e => ExError::Revert(format!("substrate error: {e:?}")),246 }247}248249pub trait WithRecorder<T: Config> {250 fn recorder(&self) -> &SubstrateRecorder<T>;251 fn into_recorder(self) -> SubstrateRecorder<T>;252}253254255pub fn call<T, C, E, H>(handle: &mut H, mut e: E) -> Option<PrecompileResult>256where257 T: Config,258 C: evm_coder::Call + PreDispatch,259 E: evm_coder::Callable<C> + WithRecorder<T>,260 H: PrecompileHandle,261 execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,262{263 let result = call_internal(264 handle.context().caller,265 &mut e,266 handle.context().apparent_value,267 handle.input(),268 );269 e.into_recorder().evm_to_precompile_output(handle, result)270}271272fn call_internal<T, C, E>(273 caller: H160,274 e: &mut E,275 value: Value,276 input: &[u8],277) -> execution::Result<Option<Vec<u8>>>278where279 T: Config,280 C: evm_coder::Call + PreDispatch,281 E: Contract + evm_coder::Callable<C> + WithRecorder<T>,282 execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,283{284 let call = C::parse_full(input)?;285 if call.is_none() {286 let selector = if input.len() >= 4 {287 let mut selector = [0; 4];288 selector.copy_from_slice(&input[..4]);289 u32::from_be_bytes(selector)290 } else {291 0292 };293 return Err(format!("unrecognized selector: 0x{selector:0>8x}").into());294 }295 let call = call.unwrap();296297 let dispatch_info = call.dispatch_info();298 e.recorder()299 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;300301 match execution::ResultWithPostInfo::from(e.call(Msg {302 call,303 caller,304 value,305 })) {306 Ok(v) => {307 let unspent = v.post_info.calc_unspent(&dispatch_info);308 e.recorder()309 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));310 Ok(Some(v.data))311 }312 Err(v) => {313 let unspent = v.post_info.calc_unspent(&dispatch_info);314 e.recorder()315 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));316 Err(v.data)317 }318 }319}320321#[cfg(test)]322#[allow(dead_code)]323mod tests {324 use core::marker::PhantomData;325326 use evm_coder::ERC165Call;327 use frame_support::weights::Weight;328329 use crate::execution::PreDispatch;330331 #[derive(PreDispatch)]332 enum ExampleCall<T: super::Config> {333 ERC165Call(ERC165Call, PhantomData<fn() -> T>),334 OtherCall(ERC165Call),335336 #[weight(Weight::from_parts(a + b, 0))]337 Example {338 a: u64,339 b: u64,340 },341 }342}