1234567891011121314151617#![cfg_attr(not(feature = "std"), no_std)]1819#[cfg(not(feature = "std"))]20extern crate alloc;21222324pub use pallet::*;2526#[frame_support::pallet]27pub mod pallet {28 #[cfg(not(feature = "std"))]29 use alloc::format;3031 use evm_coder::{32 ToLog,33 abi::{AbiReader, AbiWrite, AbiWriter},34 execution::{self, Result},35 types::{Msg, value},36 };37 use frame_support::{ensure, sp_runtime::ModuleError};38 use pallet_evm::{39 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,40 PrecompileResult, runner::stack::MaybeMirroredLog,41 };42 use frame_system::ensure_signed;43 pub use frame_support::dispatch::DispatchResult;44 use pallet_ethereum::EthereumTransactionSender;45 use sp_std::cell::RefCell;46 use sp_std::vec::Vec;47 use sp_core::H160;48 use frame_support::{pallet_prelude::*, traits::PalletInfo};49 use frame_system::pallet_prelude::*;5051 52 53 54 55 56 #[pallet::error]57 pub enum Error<T> {58 OutOfGas,59 OutOfFund,60 }6162 #[pallet::config]63 pub trait Config: frame_system::Config {64 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;65 type GasWeightMapping: pallet_evm::GasWeightMapping;66 }6768 #[pallet::pallet]69 pub struct Pallet<T>(_);7071 #[pallet::call]72 impl<T: Config> Pallet<T> {73 #[pallet::weight(0)]74 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {75 let _sender = ensure_signed(origin)?;76 Ok(())77 }78 }7980 81 pub const G_SLOAD_WORD: u64 = 800;82 pub const G_SSTORE_WORD: u64 = 20000;8384 #[derive(Default)]85 pub struct SubstrateRecorder<T: Config> {86 contract: H160,87 logs: RefCell<Vec<MaybeMirroredLog>>,88 initial_gas: u64,89 gas_limit: RefCell<u64>,90 _phantom: PhantomData<*const T>,91 }9293 impl<T: Config> SubstrateRecorder<T> {94 pub fn new(contract: H160, gas_limit: u64) -> Self {95 Self {96 contract,97 logs: RefCell::new(Vec::new()),98 initial_gas: gas_limit,99 gas_limit: RefCell::new(gas_limit),100 _phantom: PhantomData,101 }102 }103104 pub fn is_empty(&self) -> bool {105 self.logs.borrow().is_empty()106 }107 108 pub fn log_direct(&self, log: impl ToLog) {109 self.logs110 .borrow_mut()111 .push(MaybeMirroredLog::direct(log.to_log(self.contract)))112 }113 114 pub fn log_mirrored(&self, log: impl ToLog) {115 self.logs116 .borrow_mut()117 .push(MaybeMirroredLog::mirrored(log.to_log(self.contract)))118 }119 pub fn retrieve_logs(self) -> Vec<MaybeMirroredLog> {120 self.logs.into_inner()121 }122123 pub fn gas_left(&self) -> u64 {124 *self.gas_limit.borrow()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) -> Result<()> {144 self.consume_gas(G_SLOAD_WORD)145 }146 pub fn consume_sstore(&self) -> Result<()> {147 self.consume_gas(G_SSTORE_WORD)148 }149 pub fn consume_gas(&self, gas: u64) -> 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 result: evm_coder::execution::Result<Option<AbiWriter>>,168 ) -> Option<PrecompileResult> {169 use evm_coder::execution::Error;170 Some(match result {171 Ok(Some(v)) => Ok(PrecompileOutput {172 exit_status: ExitSucceed::Returned,173 cost: self.initial_gas - self.gas_left(),174 175 logs: self.retrieve_logs().into_iter().map(|l| l.log).collect(),176 output: v.finish(),177 }),178 Ok(None) => return None,179 Err(Error::Revert(e)) => {180 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));181 (&e as &str).abi_write(&mut writer);182183 Err(PrecompileFailure::Revert {184 exit_status: ExitRevert::Reverted,185 cost: self.initial_gas - self.gas_left(),186 output: writer.finish(),187 })188 }189 Err(Error::Fatal(f)) => Err(f.into()),190 Err(Error::Error(e)) => Err(e.into()),191 })192 }193194 pub fn submit_logs(self) {195 let logs = self.retrieve_logs();196 if logs.is_empty() {197 return;198 }199 T::EthereumTransactionSender::submit_logs_transaction(Default::default(), logs)200 }201 }202203 pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> evm_coder::execution::Error {204 use evm_coder::execution::Error as ExError;205 match err {206 DispatchError::Module(ModuleError { index, error, .. })207 if index208 == T::PalletInfo::index::<Pallet<T>>()209 .expect("evm-coder-substrate is a pallet, which should be added to runtime")210 as u8 =>211 {212 let mut read = &error as &[u8];213 match Error::<T>::decode(&mut read) {214 Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),215 Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),216 _ => unreachable!("this pallet only defines two possible errors"),217 }218 }219 DispatchError::Module(ModuleError {220 message: Some(msg), ..221 }) => ExError::Revert(msg.into()),222 DispatchError::Module(ModuleError { index, error, .. }) => {223 ExError::Revert(format!("error {:?} in pallet {}", error, index))224 }225 e => ExError::Revert(format!("substrate error: {:?}", e)),226 }227 }228229 pub trait WithRecorder<T: Config> {230 fn recorder(&self) -> &SubstrateRecorder<T>;231 fn into_recorder(self) -> SubstrateRecorder<T>;232 }233234 235 pub fn call<236 T: Config,237 C: evm_coder::Call + evm_coder::Weighted,238 E: evm_coder::Callable<C> + WithRecorder<T>,239 >(240 caller: H160,241 mut e: E,242 value: value,243 input: &[u8],244 ) -> Option<PrecompileResult> {245 let result = call_internal(caller, &mut e, value, input);246 e.into_recorder().evm_to_precompile_output(result)247 }248249 fn call_internal<250 T: Config,251 C: evm_coder::Call + evm_coder::Weighted,252 E: evm_coder::Callable<C> + WithRecorder<T>,253 >(254 caller: H160,255 e: &mut E,256 value: value,257 input: &[u8],258 ) -> evm_coder::execution::Result<Option<AbiWriter>> {259 let (selector, mut reader) = AbiReader::new_call(input)?;260 let call = C::parse(selector, &mut reader)?;261 if call.is_none() {262 return Ok(None);263 }264 let call = call.unwrap();265266 let dispatch_info = call.weight();267 e.recorder()268 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;269270 match e.call(Msg {271 call,272 caller,273 value,274 }) {275 Ok(v) => {276 let unspent = v.post_info.calc_unspent(&dispatch_info);277 e.recorder()278 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));279 Ok(Some(v.data))280 }281 Err(v) => {282 let unspent = v.post_info.calc_unspent(&dispatch_info);283 e.recorder()284 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));285 Err(v.data)286 }287 }288 }289}