git.delta.rocks / unique-network / refs/commits / c5e73ec4659b

difftreelog

source

pallets/evm-coder-substrate/src/lib.rs9.7 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819extern crate self as pallet_evm_coder_substrate;2021#[cfg(not(feature = "std"))]22extern crate alloc;23#[cfg(not(feature = "std"))]24use alloc::format;25use core::marker::PhantomData;2627use execution::PreDispatch;28use frame_support::{29	ensure, pallet_prelude::DispatchError, sp_runtime::ModuleError, traits::PalletInfo,30};31use pallet_evm::{32	ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileHandle,33	PrecompileOutput, PrecompileResult,34};35use parity_scale_codec::Decode;36use sp_core::{Get, H160};37use sp_std::{cell::RefCell, vec::Vec};38use sp_weights::Weight;39use up_data_structs::budget;40// #[cfg(feature = "runtime-benchmarks")]41// pub mod benchmarking;42pub mod execution;4344pub use evm_coder::{abi, solidity_interface, types, Contract, ResultWithPostInfoOf, ToLog};45use evm_coder::{46	types::{Msg, Value},47	AbiEncode,48};49pub use pallet::*;50#[doc(hidden)]51pub use spez::spez;5253#[frame_support::pallet]54pub mod pallet {55	pub use frame_support::dispatch::DispatchResult;5657	use super::*;5859	/// DispatchError is opaque, but we need to somehow extract correct error in case of OutOfGas failure60	/// So we have this pallet, which defines OutOfGas error, and knews its own id to check if DispatchError61	/// is thrown because of it62	///63	/// These errors shouldn't end in extrinsic results, as they only used in evm execution path64	#[pallet::error]65	pub enum Error<T> {66		OutOfGas,67		OutOfFund,68	}6970	#[pallet::config]71	pub trait Config: frame_system::Config + pallet_evm::Config {}7273	#[pallet::pallet]74	pub struct Pallet<T>(_);75}7677// From instabul hardfork configuration: https://github.com/rust-blockchain/evm/blob/fd4fd6acc0ca3208d6770fdb3ba407c94cdf97c6/runtime/src/lib.rs#L28478pub const G_SLOAD_WORD: u64 = 800;79pub const G_SSTORE_WORD: u64 = 20000;8081pub struct GasCallsBudget<'r, T: Config> {82	recorder: &'r SubstrateRecorder<T>,83	gas_per_call: u64,84}85impl<T: Config> budget::Budget for GasCallsBudget<'_, T> {86	fn consume_custom(&self, calls: u32) -> bool {87		let (gas, overflown) = (calls as u64).overflowing_mul(self.gas_per_call);88		if overflown {89			return false;90		}91		self.recorder.consume_gas(gas).is_ok()92	}93}9495#[derive(Default)]96pub struct SubstrateRecorder<T: Config> {97	initial_gas: u64,98	gas_limit: RefCell<u64>,99	_phantom: PhantomData<*const T>,100}101102impl<T: Config> SubstrateRecorder<T> {103	pub fn new(gas_limit: u64) -> Self {104		Self {105			initial_gas: gas_limit,106			gas_limit: RefCell::new(gas_limit),107			_phantom: PhantomData,108		}109	}110111	pub fn gas_left(&self) -> u64 {112		*self.gas_limit.borrow()113	}114	pub fn gas_calls_budget(&self, gas_per_call: u64) -> GasCallsBudget<T> {115		GasCallsBudget {116			recorder: self,117			gas_per_call,118		}119	}120	pub fn weight_calls_budget(&self, weight_per_call: Weight) -> GasCallsBudget<T> {121		GasCallsBudget {122			recorder: self,123			gas_per_call: T::GasWeightMapping::weight_to_gas(weight_per_call),124		}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) -> execution::Result<()> {144		self.consume_gas(G_SLOAD_WORD)145	}146	pub fn consume_sstore(&self) -> execution::Result<()> {147		self.consume_gas(G_SSTORE_WORD)148	}149	pub fn consume_gas(&self, gas: u64) -> execution::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		handle: &mut impl PrecompileHandle,168		result: execution::Result<Option<Vec<u8>>>,169	) -> Option<PrecompileResult> {170		use execution::Error;171		// We ignore error here, as it should not occur, as we have our own bookkeeping of gas172		let _ = handle.record_cost(self.initial_gas - self.gas_left());173		Some(match result {174			Ok(Some(v)) => Ok(PrecompileOutput {175				exit_status: ExitSucceed::Returned,176				output: v,177			}),178			Ok(None) => return None,179			Err(Error::Revert(e)) => Err(PrecompileFailure::Revert {180				exit_status: ExitRevert::Reverted,181				output: (&e as &str,).abi_encode_call(evm_coder::fn_selector!(Error(string))),182			}),183			Err(Error::Fatal(f)) => Err(PrecompileFailure::Fatal { exit_status: f }),184			Err(Error::Error(e)) => Err(e.into()),185		})186	}187188	/// Consume gas for reading.189	pub fn consume_store_reads(&self, reads: u64) -> execution::Result<()> {190		self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(191			<T as frame_system::Config>::DbWeight::get()192				.read193				.saturating_mul(reads),194			// TODO: measure proof195			0,196		)))197	}198199	/// Consume gas for writing.200	pub fn consume_store_writes(&self, writes: u64) -> execution::Result<()> {201		self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(202			<T as frame_system::Config>::DbWeight::get()203				.write204				.saturating_mul(writes),205			// TODO: measure proof206			0,207		)))208	}209210	/// Consume gas for reading and writing.211	pub fn consume_store_reads_and_writes(&self, reads: u64, writes: u64) -> execution::Result<()> {212		let weight = <T as frame_system::Config>::DbWeight::get();213		let reads = weight.read.saturating_mul(reads);214		let writes = weight.read.saturating_mul(writes);215		self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(216			reads.saturating_add(writes),217			// TODO: measure proof218			0,219		)))220	}221}222223pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> execution::Error {224	use 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			let mut read = &error as &[u8];233			match Error::<T>::decode(&mut read) {234				Ok(Error::<T>::OutOfGas) => ExError::Error(ExitError::OutOfGas),235				Ok(Error::<T>::OutOfFund) => ExError::Error(ExitError::OutOfFund),236				_ => unreachable!("this pallet only defines two possible errors"),237			}238		}239		DispatchError::Module(ModuleError {240			message: Some(msg), ..241		}) => ExError::Revert(msg.into()),242		DispatchError::Module(ModuleError { index, error, .. }) => {243			ExError::Revert(format!("error {error:?} in pallet {index}"))244		}245		e => ExError::Revert(format!("substrate error: {e:?}")),246	}247}248249pub trait WithRecorder<T: Config> {250	fn recorder(&self) -> &SubstrateRecorder<T>;251	fn into_recorder(self) -> SubstrateRecorder<T>;252}253254/// Helper to simplify implementing bridge between evm-coder definitions and pallet-evm255pub fn call<T, C, E, H>(handle: &mut H, mut e: E) -> Option<PrecompileResult>256where257	T: Config,258	C: evm_coder::Call + PreDispatch,259	E: evm_coder::Callable<C> + WithRecorder<T>,260	H: PrecompileHandle,261	execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,262{263	let result = call_internal(264		handle.context().caller,265		&mut e,266		handle.context().apparent_value,267		handle.input(),268	);269	e.into_recorder().evm_to_precompile_output(handle, result)270}271272fn call_internal<T, C, E>(273	caller: H160,274	e: &mut E,275	value: Value,276	input: &[u8],277) -> execution::Result<Option<Vec<u8>>>278where279	T: Config,280	C: evm_coder::Call + PreDispatch,281	E: Contract + evm_coder::Callable<C> + WithRecorder<T>,282	execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,283{284	let call = C::parse_full(input)?;285	if call.is_none() {286		let selector = if input.len() >= 4 {287			let mut selector = [0; 4];288			selector.copy_from_slice(&input[..4]);289			u32::from_be_bytes(selector)290		} else {291			0292		};293		return Err(format!("unrecognized selector: 0x{selector:0>8x}").into());294	}295	let call = call.unwrap();296297	let dispatch_info = call.dispatch_info();298	e.recorder()299		.consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;300301	match execution::ResultWithPostInfo::from(e.call(Msg {302		call,303		caller,304		value,305	})) {306		Ok(v) => {307			let unspent = v.post_info.calc_unspent(&dispatch_info);308			e.recorder()309				.return_gas(T::GasWeightMapping::weight_to_gas(unspent));310			Ok(Some(v.data))311		}312		Err(v) => {313			let unspent = v.post_info.calc_unspent(&dispatch_info);314			e.recorder()315				.return_gas(T::GasWeightMapping::weight_to_gas(unspent));316			Err(v.data)317		}318	}319}320321#[cfg(test)]322#[allow(dead_code)]323mod tests {324	use core::marker::PhantomData;325326	use evm_coder::ERC165Call;327	use frame_support::weights::Weight;328329	use crate::execution::PreDispatch;330331	#[derive(PreDispatch)]332	enum ExampleCall<T: super::Config> {333		ERC165Call(ERC165Call, PhantomData<fn() -> T>),334		OtherCall(ERC165Call),335336		#[weight(Weight::from_parts(a + b, 0))]337		Example {338			a: u64,339			b: u64,340		},341	}342}