1// This file is part of Frontier.
23// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
67// 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.
1819use crate::{
20 solidity::{
21 codec::Reader,
22 modifier::FunctionModifier,
23 revert::{MayRevert, RevertReason},
24 },
25 EvmResult,
26};
27use fp_evm::{Log, PrecompileHandle};
2829pub 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.
32fn record_db_read<Runtime: pallet_evm::Config>(
33&mut self,
34 data_max_encoded_len: usize,
35 ) -> Result<(), evm::ExitError>;
3637/// Record cost of a log manually.
38 /// This can be useful to record log costs early when their content have static size.
39fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult;
4041/// Record cost of logs.
42fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult;
4344/// Check that a function call is compatible with the context it is
45 /// called into.
46fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert;
4748/// Read the selector from the input data.
49fn read_u32_selector(&self) -> MayRevert<u32>;
5051/// Returns a reader of the input, skipping the selector.
52fn read_after_selector(&self) -> MayRevert<Reader>;
53}
5455impl<T: PrecompileHandle> PrecompileHandleExt for T {
56fn record_db_read<Runtime: pallet_evm::Config>(
57&mut self,
58 data_max_encoded_len: usize,
59 ) -> Result<(), evm::ExitError> {
60self.record_cost(crate::prelude::RuntimeHelper::<Runtime>::db_read_gas_cost())?;
61// TODO: record ref time when precompile will be benchmarked
62self.record_external_cost(None, Some(data_max_encoded_len as u64), None)
63 }
6465/// Record cost of a log manually.
66 /// This can be useful to record log costs early when their content have static size.
67fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult {
68self.record_cost(crate::evm::costs::log_costs(topics, data_len)?)?;
6970Ok(())
71 }
7273/// Record cost of logs.
74fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult {
75for log in logs {
76self.record_log_costs_manual(log.topics.len(), log.data.len())?;
77 }
7879Ok(())
80 }
8182/// Check that a function call is compatible with the context it is
83 /// called into.
84fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert {
85crate::solidity::modifier::check_function_modifier(
86self.context(),
87self.is_static(),
88 modifier,
89 )
90 }
9192/// Read the selector from the input data as u32.
93fn read_u32_selector(&self) -> MayRevert<u32> {
94crate::solidity::codec::selector(self.input())
95 .ok_or(RevertReason::read_out_of_bounds("selector").into())
96 }
9798/// Returns a reader of the input, skipping the selector.
99fn read_after_selector(&self) -> MayRevert<Reader> {
100 Reader::new_skip_selector(self.input())
101 }
102}
103104environmental::environmental!(EVM_CONTEXT: trait PrecompileHandle);
105106pub 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).
119unsafe {
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}
128129pub 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}
132133#[cfg(test)]
134mod tests {
135use super::*;
136137struct MockPrecompileHandle;
138impl PrecompileHandle for MockPrecompileHandle {
139fn 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>) {
148unimplemented!()
149 }
150151fn record_cost(&mut self, _: u64) -> Result<(), evm::ExitError> {
152unimplemented!()
153 }
154155fn remaining_gas(&self) -> u64 {
156unimplemented!()
157 }
158159fn log(
160&mut self,
161_: sp_core::H160,
162_: Vec<sp_core::H256>,
163_: Vec<u8>,
164 ) -> Result<(), evm::ExitError> {
165unimplemented!()
166 }
167168fn code_address(&self) -> sp_core::H160 {
169unimplemented!()
170 }
171172fn input(&self) -> &[u8] {
173unimplemented!()
174 }
175176fn context(&self) -> &evm::Context {
177unimplemented!()
178 }
179180fn origin(&self) -> sp_core::H160 {
181unimplemented!()
182 }
183184fn is_static(&self) -> bool {
185true
186}
187188fn gas_limit(&self) -> Option<u64> {
189unimplemented!()
190 }
191192fn 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> {
198Ok(())
199 }
200201fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
202203fn is_contract_being_constructed(&self, _address: sp_core::H160) -> bool {
204unimplemented!()
205 }
206 }
207208#[test]
209fn with_precompile_handle_without_context() {
210assert_eq!(with_precompile_handle(|_| {}), None);
211 }
212213#[test]
214fn with_precompile_handle_with_context() {
215let mut precompile_handle = MockPrecompileHandle;
216217assert_eq!(
218 using_precompile_handle(&mut precompile_handle, || with_precompile_handle(
219 |handle| handle.is_static()
220 )),
221Some(true)
222 );
223 }
224}