precompile_utils/evm/
handle.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19use crate::{
20	solidity::{
21		codec::Reader,
22		modifier::FunctionModifier,
23		revert::{MayRevert, RevertReason},
24	},
25	EvmResult,
26};
27use fp_evm::{Log, PrecompileHandle};
28
29pub trait PrecompileHandleExt: PrecompileHandle {
30	/// Record cost of one DB read manually.
31	/// The max encoded length of the data that will be read should be provided.
32	fn record_db_read<Runtime: pallet_evm::Config>(
33		&mut self,
34		data_max_encoded_len: usize,
35	) -> Result<(), evm::ExitError>;
36
37	/// Record cost of a log manually.
38	/// This can be useful to record log costs early when their content have static size.
39	fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult;
40
41	/// Record cost of logs.
42	fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult;
43
44	/// Check that a function call is compatible with the context it is
45	/// called into.
46	fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert;
47
48	/// Read the selector from the input data.
49	fn read_u32_selector(&self) -> MayRevert<u32>;
50
51	/// Returns a reader of the input, skipping the selector.
52	fn read_after_selector(&self) -> MayRevert<Reader>;
53}
54
55impl<T: PrecompileHandle> PrecompileHandleExt for T {
56	fn record_db_read<Runtime: pallet_evm::Config>(
57		&mut self,
58		data_max_encoded_len: usize,
59	) -> Result<(), evm::ExitError> {
60		self.record_cost(crate::prelude::RuntimeHelper::<Runtime>::db_read_gas_cost())?;
61		// TODO: record ref time when precompile will be benchmarked
62		self.record_external_cost(None, Some(data_max_encoded_len as u64), None)
63	}
64
65	/// Record cost of a log manually.
66	/// This can be useful to record log costs early when their content have static size.
67	fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult {
68		self.record_cost(crate::evm::costs::log_costs(topics, data_len)?)?;
69
70		Ok(())
71	}
72
73	/// Record cost of logs.
74	fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult {
75		for log in logs {
76			self.record_log_costs_manual(log.topics.len(), log.data.len())?;
77		}
78
79		Ok(())
80	}
81
82	/// Check that a function call is compatible with the context it is
83	/// called into.
84	fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert {
85		crate::solidity::modifier::check_function_modifier(
86			self.context(),
87			self.is_static(),
88			modifier,
89		)
90	}
91
92	/// Read the selector from the input data as u32.
93	fn read_u32_selector(&self) -> MayRevert<u32> {
94		crate::solidity::codec::selector(self.input())
95			.ok_or(RevertReason::read_out_of_bounds("selector").into())
96	}
97
98	/// Returns a reader of the input, skipping the selector.
99	fn read_after_selector(&self) -> MayRevert<Reader> {
100		Reader::new_skip_selector(self.input())
101	}
102}
103
104environmental::environmental!(EVM_CONTEXT: trait PrecompileHandle);
105
106pub fn using_precompile_handle<'a, R, F: FnOnce() -> R>(
107	precompile_handle: &'a mut dyn PrecompileHandle,
108	mutator: F,
109) -> R {
110	// # Safety
111	//
112	// unsafe rust does not mean unsafe, but "the compiler cannot guarantee the safety of the
113	// memory".
114	//
115	// The only risk here is that the lifetime 'a comes to its end while the global variable
116	// `EVM_CONTEXT` still contains the reference to the precompile handle.
117	// The `using` method guarantee that it can't happen because the global variable is freed right
118	// after the execution of the `mutator` closure (whatever the result of the execution).
119	unsafe {
120		EVM_CONTEXT::using(
121			core::mem::transmute::<&'a mut dyn PrecompileHandle, &'static mut dyn PrecompileHandle>(
122				precompile_handle,
123			),
124			mutator,
125		)
126	}
127}
128
129pub fn with_precompile_handle<R, F: FnOnce(&mut dyn PrecompileHandle) -> R>(f: F) -> Option<R> {
130	EVM_CONTEXT::with(|precompile_handle| f(precompile_handle))
131}
132
133#[cfg(test)]
134mod tests {
135	use super::*;
136
137	struct MockPrecompileHandle;
138	impl PrecompileHandle for MockPrecompileHandle {
139		fn call(
140			&mut self,
141			_: sp_core::H160,
142			_: Option<evm::Transfer>,
143			_: Vec<u8>,
144			_: Option<u64>,
145			_: bool,
146			_: &evm::Context,
147		) -> (evm::ExitReason, Vec<u8>) {
148			unimplemented!()
149		}
150
151		fn record_cost(&mut self, _: u64) -> Result<(), evm::ExitError> {
152			unimplemented!()
153		}
154
155		fn remaining_gas(&self) -> u64 {
156			unimplemented!()
157		}
158
159		fn log(
160			&mut self,
161			_: sp_core::H160,
162			_: Vec<sp_core::H256>,
163			_: Vec<u8>,
164		) -> Result<(), evm::ExitError> {
165			unimplemented!()
166		}
167
168		fn code_address(&self) -> sp_core::H160 {
169			unimplemented!()
170		}
171
172		fn input(&self) -> &[u8] {
173			unimplemented!()
174		}
175
176		fn context(&self) -> &evm::Context {
177			unimplemented!()
178		}
179
180		fn origin(&self) -> sp_core::H160 {
181			unimplemented!()
182		}
183
184		fn is_static(&self) -> bool {
185			true
186		}
187
188		fn gas_limit(&self) -> Option<u64> {
189			unimplemented!()
190		}
191
192		fn record_external_cost(
193			&mut self,
194			_ref_time: Option<u64>,
195			_proof_size: Option<u64>,
196			_storage_growth: Option<u64>,
197		) -> Result<(), fp_evm::ExitError> {
198			Ok(())
199		}
200
201		fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
202
203		fn is_contract_being_constructed(&self, _address: sp_core::H160) -> bool {
204			unimplemented!()
205		}
206	}
207
208	#[test]
209	fn with_precompile_handle_without_context() {
210		assert_eq!(with_precompile_handle(|_| {}), None);
211	}
212
213	#[test]
214	fn with_precompile_handle_with_context() {
215		let mut precompile_handle = MockPrecompileHandle;
216
217		assert_eq!(
218			using_precompile_handle(&mut precompile_handle, || with_precompile_handle(
219				|handle| handle.is_static()
220			)),
221			Some(true)
222		);
223	}
224}