1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(not(feature = "std"))]4extern crate alloc;5678pub use pallet::*;910#[frame_support::pallet]11pub mod pallet {12 #[cfg(not(feature = "std"))]13 use alloc::format;1415 use evm_coder::{16 ToLog,17 abi::{AbiReader, AbiWrite, AbiWriter},18 execution::{self, Result},19 types::{Msg, value},20 };21 use frame_support::{ensure};22 use pallet_evm::{23 ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,24 PrecompileResult, runner::stack::MaybeMirroredLog,25 };26 use frame_system::ensure_signed;27 pub use frame_support::dispatch::DispatchResult;28 use pallet_ethereum::EthereumTransactionSender;29 use sp_std::cell::RefCell;30 use sp_std::vec::Vec;31 use sp_core::{H160, H256};32 use ethereum::TransactionV2;33 use frame_support::{pallet_prelude::*, traits::PalletInfo};34 use frame_system::pallet_prelude::*;3536 37 38 39 40 41 #[pallet::error]42 pub enum Error<T> {43 OutOfGas,44 OutOfFund,45 }4647 #[pallet::config]48 pub trait Config: frame_system::Config {49 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;50 type GasWeightMapping: pallet_evm::GasWeightMapping;51 }5253 #[pallet::pallet]54 pub struct Pallet<T>(_);5556 #[pallet::call]57 impl<T: Config> Pallet<T> {58 #[pallet::weight(0)]59 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {60 let _sender = ensure_signed(origin)?;61 Ok(())62 }63 }6465 66 pub const G_SLOAD_WORD: u64 = 800;67 pub const G_SSTORE_WORD: u64 = 20000;6869 pub fn generate_transaction() -> TransactionV2 {70 use ethereum::{TransactionV0, TransactionAction, TransactionSignature};71 TransactionV2::Legacy(TransactionV0 {72 nonce: 0.into(),73 gas_price: 0.into(),74 gas_limit: 0.into(),75 action: TransactionAction::Call(H160([0; 20])),76 value: 0.into(),77 78 input: Vec::from([0, 0, 0, 0]),79 80 signature: TransactionSignature::new(27, H256([0x88; 32]), H256([0x88; 32])).unwrap(),81 })82 }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(200 Default::default(),201 generate_transaction(),202 logs,203 )204 }205 }206207 pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> evm_coder::execution::Error {208 use evm_coder::execution::Error as ExError;209 match err {210 DispatchError::Module { index, error, .. }211 if index212 == T::PalletInfo::index::<Pallet<T>>()213 .expect("evm-coder-substrate is a pallet, which should be added to runtime")214 as u8 =>215 {216 match error {217 v if v == Error::<T>::OutOfGas.as_u8() => ExError::Error(ExitError::OutOfGas),218 v if v == Error::<T>::OutOfFund.as_u8() => ExError::Error(ExitError::OutOfFund),219 _ => unreachable!("this pallet only defines two possible errors"),220 }221 }222 DispatchError::Module {223 message: Some(msg), ..224 } => ExError::Revert(msg.into()),225 DispatchError::Module { index, error, .. } => {226 ExError::Revert(format!("error {} in pallet {}", error, index))227 }228 e => ExError::Revert(format!("substrate error: {:?}", e)),229 }230 }231232 pub trait WithRecorder<T: Config> {233 fn recorder(&self) -> &SubstrateRecorder<T>;234 fn into_recorder(self) -> SubstrateRecorder<T>;235 }236237 238 pub fn call<239 T: Config,240 C: evm_coder::Call + evm_coder::Weighted,241 E: evm_coder::Callable<C> + WithRecorder<T>,242 >(243 caller: H160,244 mut e: E,245 value: value,246 input: &[u8],247 ) -> Option<PrecompileResult> {248 let result = call_internal(caller, &mut e, value, input);249 e.into_recorder().evm_to_precompile_output(result)250 }251252 fn call_internal<253 T: Config,254 C: evm_coder::Call + evm_coder::Weighted,255 E: evm_coder::Callable<C> + WithRecorder<T>,256 >(257 caller: H160,258 e: &mut E,259 value: value,260 input: &[u8],261 ) -> evm_coder::execution::Result<Option<AbiWriter>> {262 let (selector, mut reader) = AbiReader::new_call(input)?;263 let call = C::parse(selector, &mut reader)?;264 if call.is_none() {265 return Ok(None);266 }267 let call = call.unwrap();268269 let dispatch_info = call.weight();270 e.recorder()271 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;272273 match e.call(Msg {274 call,275 caller,276 value,277 }) {278 Ok(v) => {279 let unspent = v.post_info.calc_unspent(&dispatch_info);280 e.recorder()281 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));282 Ok(Some(v.data))283 }284 Err(v) => {285 let unspent = v.post_info.calc_unspent(&dispatch_info);286 e.recorder()287 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));288 Err(v.data)289 }290 }291 }292}