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,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::Log;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() -> ethereum::TransactionV0 {70 use ethereum::{TransactionV0, TransactionAction, TransactionSignature};71 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<Log>>,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 109 110 pub fn log(&self, log: impl ToLog) {111 self.logs.borrow_mut().push(log.to_log(self.contract));112 }113 pub fn retrieve_logs(self) -> Vec<Log> {114 self.logs.into_inner()115 }116117 pub fn gas_left(&self) -> u64 {118 *self.gas_limit.borrow()119 }120 pub fn consume_sload_sub(&self) -> DispatchResult {121 self.consume_gas_sub(G_SLOAD_WORD)122 }123 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {124 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))125 }126 pub fn consume_sstore_sub(&self) -> DispatchResult {127 self.consume_gas_sub(G_SSTORE_WORD)128 }129 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {130 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);131 let mut gas_limit = self.gas_limit.borrow_mut();132 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);133 *gas_limit -= gas;134 Ok(())135 }136137 pub fn consume_sload(&self) -> Result<()> {138 self.consume_gas(G_SLOAD_WORD)139 }140 pub fn consume_sstore(&self) -> Result<()> {141 self.consume_gas(G_SSTORE_WORD)142 }143 pub fn consume_gas(&self, gas: u64) -> Result<()> {144 if gas == u64::MAX {145 return Err(execution::Error::Error(ExitError::OutOfGas));146 }147 let mut gas_limit = self.gas_limit.borrow_mut();148 if gas > *gas_limit {149 return Err(execution::Error::Error(ExitError::OutOfGas));150 }151 *gas_limit -= gas;152 Ok(())153 }154 pub fn return_gas(&self, gas: u64) {155 let mut gas_limit = self.gas_limit.borrow_mut();156 *gas_limit += gas;157 }158159 pub fn evm_to_precompile_output(160 self,161 result: evm_coder::execution::Result<Option<AbiWriter>>,162 ) -> Option<PrecompileResult> {163 use evm_coder::execution::Error;164 Some(match result {165 Ok(Some(v)) => Ok(PrecompileOutput {166 exit_status: ExitSucceed::Returned,167 cost: self.initial_gas - self.gas_left(),168 logs: self.retrieve_logs(),169 output: v.finish(),170 }),171 Ok(None) => return None,172 Err(Error::Revert(e)) => {173 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));174 (&e as &str).abi_write(&mut writer);175176 Err(PrecompileFailure::Revert {177 exit_status: ExitRevert::Reverted,178 cost: self.initial_gas - self.gas_left(),179 output: writer.finish(),180 })181 }182 Err(Error::Fatal(f)) => Err(f.into()),183 Err(Error::Error(e)) => Err(e.into()),184 })185 }186187 pub fn submit_logs(self) {188 let logs = self.retrieve_logs();189 if logs.is_empty() {190 return;191 }192 T::EthereumTransactionSender::submit_logs_transaction(193 Default::default(),194 generate_transaction(),195 logs,196 )197 }198 }199200 pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> evm_coder::execution::Error {201 use evm_coder::execution::Error as ExError;202 match err {203 DispatchError::Module { index, error, .. }204 if index205 == T::PalletInfo::index::<Pallet<T>>()206 .expect("evm-coder-substrate is a pallet, which should be added to runtime")207 as u8 =>208 {209 match error {210 v if v == Error::<T>::OutOfGas.as_u8() => ExError::Error(ExitError::OutOfGas),211 v if v == Error::<T>::OutOfFund.as_u8() => ExError::Error(ExitError::OutOfFund),212 _ => unreachable!("this pallet only defines two possible errors"),213 }214 }215 DispatchError::Module {216 message: Some(msg), ..217 } => ExError::Revert(msg.into()),218 DispatchError::Module { index, error, .. } => {219 ExError::Revert(format!("error {} in pallet {}", error, index))220 }221 e => ExError::Revert(format!("substrate error: {:?}", e)),222 }223 }224225 pub trait WithRecorder<T: Config> {226 fn recorder(&self) -> &SubstrateRecorder<T>;227 fn into_recorder(self) -> SubstrateRecorder<T>;228 }229230 231 pub fn call<232 T: Config,233 C: evm_coder::Call + evm_coder::Weighted,234 E: evm_coder::Callable<C> + WithRecorder<T>,235 >(236 caller: H160,237 mut e: E,238 value: value,239 input: &[u8],240 ) -> Option<PrecompileResult> {241 let result = call_internal(caller, &mut e, value, input);242 e.into_recorder().evm_to_precompile_output(result)243 }244245 fn call_internal<246 T: Config,247 C: evm_coder::Call + evm_coder::Weighted,248 E: evm_coder::Callable<C> + WithRecorder<T>,249 >(250 caller: H160,251 e: &mut E,252 value: value,253 input: &[u8],254 ) -> evm_coder::execution::Result<Option<AbiWriter>> {255 let (selector, mut reader) = AbiReader::new_call(input)?;256 let call = C::parse(selector, &mut reader)?;257 if call.is_none() {258 return Ok(None);259 }260 let call = call.unwrap();261262 let dispatch_info = call.weight();263 e.recorder()264 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;265266 match e.call(Msg {267 call,268 caller,269 value,270 }) {271 Ok(v) => {272 let unspent = v.post_info.calc_unspent(&dispatch_info);273 e.recorder()274 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));275 Ok(Some(v.data))276 }277 Err(v) => {278 let unspent = v.post_info.calc_unspent(&dispatch_info);279 e.recorder()280 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));281 Err(v.data)282 }283 }284 }285}