1234567891011121314151617#![cfg_attr(not(feature = "std"), no_std)]1819#[cfg(not(feature = "std"))]20extern crate alloc;21#[cfg(not(feature = "std"))]22use alloc::format;23use frame_support::dispatch::Weight;2425use core::marker::PhantomData;26use sp_std::cell::RefCell;2728use codec::Decode;29use frame_support::pallet_prelude::DispatchError;30use frame_support::traits::PalletInfo;31use frame_support::{ensure, sp_runtime::ModuleError};32use up_data_structs::budget;33use pallet_evm::{34 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,35 PrecompileResult,36};37use sp_core::H160;38394041use evm_coder::{42 abi::{AbiReader, AbiWrite, AbiWriter},43 execution::{self, Result},44 types::{Msg, value},45};4647pub use pallet::*;4849#[frame_support::pallet]50pub mod pallet {51 use super::*;5253 use frame_system::ensure_signed;54 pub use frame_support::dispatch::DispatchResult;55 use frame_system::pallet_prelude::*;5657 58 59 60 61 62 #[pallet::error]63 pub enum Error<T> {64 OutOfGas,65 OutOfFund,66 }6768 #[pallet::config]69 pub trait Config: frame_system::Config {70 type GasWeightMapping: pallet_evm::GasWeightMapping;71 }7273 #[pallet::pallet]74 pub struct Pallet<T>(_);7576 #[pallet::call]77 impl<T: Config> Pallet<T> {78 #[pallet::weight(0)]79 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {80 let _sender = ensure_signed(origin)?;81 Ok(())82 }83 }84}858687pub const G_SLOAD_WORD: u64 = 800;88pub const G_SSTORE_WORD: u64 = 20000;8990pub struct GasCallsBudget<'r, T: Config> {91 recorder: &'r SubstrateRecorder<T>,92 gas_per_call: u64,93}94impl<T: Config> budget::Budget for GasCallsBudget<'_, T> {95 fn consume_custom(&self, calls: u32) -> bool {96 let (gas, overflown) = (calls as u64).overflowing_add(self.gas_per_call);97 if overflown {98 return false;99 }100 self.recorder.consume_gas(gas).is_ok()101 }102}103104#[derive(Default)]105pub struct SubstrateRecorder<T: Config> {106 initial_gas: u64,107 gas_limit: RefCell<u64>,108 _phantom: PhantomData<*const T>,109}110111impl<T: Config> SubstrateRecorder<T> {112 pub fn new(gas_limit: u64) -> Self {113 Self {114 initial_gas: gas_limit,115 gas_limit: RefCell::new(gas_limit),116 _phantom: PhantomData,117 }118 }119120 pub fn gas_left(&self) -> u64 {121 *self.gas_limit.borrow()122 }123 pub fn gas_calls_budget(&self, gas_per_call: u64) -> GasCallsBudget<T> {124 GasCallsBudget {125 recorder: self,126 gas_per_call,127 }128 }129 pub fn weight_calls_budget(&self, weight_per_call: Weight) -> GasCallsBudget<T> {130 GasCallsBudget {131 recorder: self,132 gas_per_call: T::GasWeightMapping::weight_to_gas(weight_per_call),133 }134 }135 pub fn consume_sload_sub(&self) -> DispatchResult {136 self.consume_gas_sub(G_SLOAD_WORD)137 }138 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {139 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))140 }141 pub fn consume_sstore_sub(&self) -> DispatchResult {142 self.consume_gas_sub(G_SSTORE_WORD)143 }144 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {145 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);146 let mut gas_limit = self.gas_limit.borrow_mut();147 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);148 *gas_limit -= gas;149 Ok(())150 }151152 pub fn consume_sload(&self) -> Result<()> {153 self.consume_gas(G_SLOAD_WORD)154 }155 pub fn consume_sstore(&self) -> Result<()> {156 self.consume_gas(G_SSTORE_WORD)157 }158 pub fn consume_gas(&self, gas: u64) -> Result<()> {159 if gas == u64::MAX {160 return Err(execution::Error::Error(ExitError::OutOfGas));161 }162 let mut gas_limit = self.gas_limit.borrow_mut();163 if gas > *gas_limit {164 return Err(execution::Error::Error(ExitError::OutOfGas));165 }166 *gas_limit -= gas;167 Ok(())168 }169 pub fn return_gas(&self, gas: u64) {170 let mut gas_limit = self.gas_limit.borrow_mut();171 *gas_limit += gas;172 }173174 pub fn evm_to_precompile_output(175 self,176 result: evm_coder::execution::Result<Option<AbiWriter>>,177 ) -> Option<PrecompileResult> {178 use evm_coder::execution::Error;179 Some(match result {180 Ok(Some(v)) => Ok(PrecompileOutput {181 exit_status: ExitSucceed::Returned,182 cost: self.initial_gas - self.gas_left(),183 184 logs: sp_std::vec![],185 output: v.finish(),186 }),187 Ok(None) => return None,188 Err(Error::Revert(e)) => {189 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));190 (&e as &str).abi_write(&mut writer);191192 Err(PrecompileFailure::Revert {193 exit_status: ExitRevert::Reverted,194 cost: self.initial_gas - self.gas_left(),195 output: writer.finish(),196 })197 }198 Err(Error::Fatal(f)) => Err(f.into()),199 Err(Error::Error(e)) => Err(e.into()),200 })201 }202}203204pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> evm_coder::execution::Error {205 use evm_coder::execution::Error as ExError;206 match err {207 DispatchError::Module(ModuleError { index, error, .. })208 if index209 == T::PalletInfo::index::<Pallet<T>>()210 .expect("evm-coder-substrate is a pallet, which should be added to runtime")211 as u8 =>212 {213 let mut read = &error as &[u8];214 match Error::<T>::decode(&mut read) {215 Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),216 Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),217 _ => unreachable!("this pallet only defines two possible errors"),218 }219 }220 DispatchError::Module(ModuleError {221 message: Some(msg), ..222 }) => ExError::Revert(msg.into()),223 DispatchError::Module(ModuleError { index, error, .. }) => {224 ExError::Revert(format!("error {:?} in pallet {}", error, index))225 }226 e => ExError::Revert(format!("substrate error: {:?}", e)),227 }228}229230pub trait WithRecorder<T: Config> {231 fn recorder(&self) -> &SubstrateRecorder<T>;232 fn into_recorder(self) -> SubstrateRecorder<T>;233}234235236pub fn call<237 T: Config,238 C: evm_coder::Call + evm_coder::Weighted,239 E: evm_coder::Callable<C> + WithRecorder<T>,240>(241 caller: H160,242 mut e: E,243 value: value,244 input: &[u8],245) -> Option<PrecompileResult> {246 let result = call_internal(caller, &mut e, value, input);247 e.into_recorder().evm_to_precompile_output(result)248}249250fn call_internal<251 T: Config,252 C: evm_coder::Call + evm_coder::Weighted,253 E: evm_coder::Callable<C> + WithRecorder<T>,254>(255 caller: H160,256 e: &mut E,257 value: value,258 input: &[u8],259) -> evm_coder::execution::Result<Option<AbiWriter>> {260 let (selector, mut reader) = AbiReader::new_call(input)?;261 let call = C::parse(selector, &mut reader)?;262 if call.is_none() {263 return Ok(None);264 }265 let call = call.unwrap();266267 let dispatch_info = call.weight();268 e.recorder()269 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;270271 match e.call(Msg {272 call,273 caller,274 value,275 }) {276 Ok(v) => {277 let unspent = v.post_info.calc_unspent(&dispatch_info);278 e.recorder()279 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));280 Ok(Some(v.data))281 }282 Err(v) => {283 let unspent = v.post_info.calc_unspent(&dispatch_info);284 e.recorder()285 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));286 Err(v.data)287 }288 }289}