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, H256};48 use ethereum::TransactionV2;49 use frame_support::{pallet_prelude::*, traits::PalletInfo};50 use frame_system::pallet_prelude::*;5152 53 54 55 56 57 #[pallet::error]58 pub enum Error<T> {59 OutOfGas,60 OutOfFund,61 }6263 #[pallet::config]64 pub trait Config: frame_system::Config {65 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;66 type GasWeightMapping: pallet_evm::GasWeightMapping;67 }6869 #[pallet::pallet]70 pub struct Pallet<T>(_);7172 #[pallet::call]73 impl<T: Config> Pallet<T> {74 #[pallet::weight(0)]75 pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {76 let _sender = ensure_signed(origin)?;77 Ok(())78 }79 }8081 82 pub const G_SLOAD_WORD: u64 = 800;83 pub const G_SSTORE_WORD: u64 = 20000;8485 pub fn generate_transaction() -> TransactionV2 {86 use ethereum::{TransactionV0, TransactionAction, TransactionSignature};87 TransactionV2::Legacy(TransactionV0 {88 nonce: 0.into(),89 gas_price: 0.into(),90 gas_limit: 0.into(),91 action: TransactionAction::Call(H160([0; 20])),92 value: 0.into(),93 94 input: Vec::from([0, 0, 0, 0]),95 96 signature: TransactionSignature::new(27, H256([0x88; 32]), H256([0x88; 32])).unwrap(),97 })98 }99100 #[derive(Default)]101 pub struct SubstrateRecorder<T: Config> {102 contract: H160,103 logs: RefCell<Vec<MaybeMirroredLog>>,104 initial_gas: u64,105 gas_limit: RefCell<u64>,106 _phantom: PhantomData<*const T>,107 }108109 impl<T: Config> SubstrateRecorder<T> {110 pub fn new(contract: H160, gas_limit: u64) -> Self {111 Self {112 contract,113 logs: RefCell::new(Vec::new()),114 initial_gas: gas_limit,115 gas_limit: RefCell::new(gas_limit),116 _phantom: PhantomData,117 }118 }119120 pub fn is_empty(&self) -> bool {121 self.logs.borrow().is_empty()122 }123 124 pub fn log_direct(&self, log: impl ToLog) {125 self.logs126 .borrow_mut()127 .push(MaybeMirroredLog::direct(log.to_log(self.contract)))128 }129 130 pub fn log_mirrored(&self, log: impl ToLog) {131 self.logs132 .borrow_mut()133 .push(MaybeMirroredLog::mirrored(log.to_log(self.contract)))134 }135 pub fn retrieve_logs(self) -> Vec<MaybeMirroredLog> {136 self.logs.into_inner()137 }138139 pub fn gas_left(&self) -> u64 {140 *self.gas_limit.borrow()141 }142 pub fn consume_sload_sub(&self) -> DispatchResult {143 self.consume_gas_sub(G_SLOAD_WORD)144 }145 pub fn consume_sstores_sub(&self, amount: usize) -> DispatchResult {146 self.consume_gas_sub(G_SSTORE_WORD.saturating_mul(amount as u64))147 }148 pub fn consume_sstore_sub(&self) -> DispatchResult {149 self.consume_gas_sub(G_SSTORE_WORD)150 }151 pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {152 ensure!(gas != u64::MAX, Error::<T>::OutOfGas);153 let mut gas_limit = self.gas_limit.borrow_mut();154 ensure!(gas <= *gas_limit, Error::<T>::OutOfGas);155 *gas_limit -= gas;156 Ok(())157 }158159 pub fn consume_sload(&self) -> Result<()> {160 self.consume_gas(G_SLOAD_WORD)161 }162 pub fn consume_sstore(&self) -> Result<()> {163 self.consume_gas(G_SSTORE_WORD)164 }165 pub fn consume_gas(&self, gas: u64) -> Result<()> {166 if gas == u64::MAX {167 return Err(execution::Error::Error(ExitError::OutOfGas));168 }169 let mut gas_limit = self.gas_limit.borrow_mut();170 if gas > *gas_limit {171 return Err(execution::Error::Error(ExitError::OutOfGas));172 }173 *gas_limit -= gas;174 Ok(())175 }176 pub fn return_gas(&self, gas: u64) {177 let mut gas_limit = self.gas_limit.borrow_mut();178 *gas_limit += gas;179 }180181 pub fn evm_to_precompile_output(182 self,183 result: evm_coder::execution::Result<Option<AbiWriter>>,184 ) -> Option<PrecompileResult> {185 use evm_coder::execution::Error;186 Some(match result {187 Ok(Some(v)) => Ok(PrecompileOutput {188 exit_status: ExitSucceed::Returned,189 cost: self.initial_gas - self.gas_left(),190 191 logs: self.retrieve_logs().into_iter().map(|l| l.log).collect(),192 output: v.finish(),193 }),194 Ok(None) => return None,195 Err(Error::Revert(e)) => {196 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));197 (&e as &str).abi_write(&mut writer);198199 Err(PrecompileFailure::Revert {200 exit_status: ExitRevert::Reverted,201 cost: self.initial_gas - self.gas_left(),202 output: writer.finish(),203 })204 }205 Err(Error::Fatal(f)) => Err(f.into()),206 Err(Error::Error(e)) => Err(e.into()),207 })208 }209210 pub fn submit_logs(self) {211 let logs = self.retrieve_logs();212 if logs.is_empty() {213 return;214 }215 T::EthereumTransactionSender::submit_logs_transaction(216 Default::default(),217 generate_transaction(),218 logs,219 )220 }221 }222223 pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> evm_coder::execution::Error {224 use evm_coder::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 match error {233 v if v == Error::<T>::OutOfGas.as_u8() => ExError::Error(ExitError::OutOfGas),234 v if v == Error::<T>::OutOfFund.as_u8() => ExError::Error(ExitError::OutOfFund),235 _ => unreachable!("this pallet only defines two possible errors"),236 }237 }238 DispatchError::Module(ModuleError {239 message: Some(msg), ..240 }) => ExError::Revert(msg.into()),241 DispatchError::Module(ModuleError { index, error, .. }) => {242 ExError::Revert(format!("error {} in pallet {}", error, index))243 }244 e => ExError::Revert(format!("substrate error: {:?}", e)),245 }246 }247248 pub trait WithRecorder<T: Config> {249 fn recorder(&self) -> &SubstrateRecorder<T>;250 fn into_recorder(self) -> SubstrateRecorder<T>;251 }252253 254 pub fn call<255 T: Config,256 C: evm_coder::Call + evm_coder::Weighted,257 E: evm_coder::Callable<C> + WithRecorder<T>,258 >(259 caller: H160,260 mut e: E,261 value: value,262 input: &[u8],263 ) -> Option<PrecompileResult> {264 let result = call_internal(caller, &mut e, value, input);265 e.into_recorder().evm_to_precompile_output(result)266 }267268 fn call_internal<269 T: Config,270 C: evm_coder::Call + evm_coder::Weighted,271 E: evm_coder::Callable<C> + WithRecorder<T>,272 >(273 caller: H160,274 e: &mut E,275 value: value,276 input: &[u8],277 ) -> evm_coder::execution::Result<Option<AbiWriter>> {278 let (selector, mut reader) = AbiReader::new_call(input)?;279 let call = C::parse(selector, &mut reader)?;280 if call.is_none() {281 return Ok(None);282 }283 let call = call.unwrap();284285 let dispatch_info = call.weight();286 e.recorder()287 .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;288289 match e.call(Msg {290 call,291 caller,292 value,293 }) {294 Ok(v) => {295 let unspent = v.post_info.calc_unspent(&dispatch_info);296 e.recorder()297 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));298 Ok(Some(v.data))299 }300 Err(v) => {301 let unspent = v.post_info.calc_unspent(&dispatch_info);302 e.recorder()303 .return_gas(T::GasWeightMapping::weight_to_gas(unspent));304 Err(v.data)305 }306 }307 }308}