precompile_utils/evm/
handle.rs1use 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 fn record_db_read<Runtime: pallet_evm::Config>(
33 &mut self,
34 data_max_encoded_len: usize,
35 ) -> Result<(), evm::ExitError>;
36
37 fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult;
40
41 fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult;
43
44 fn check_function_modifier(&self, modifier: FunctionModifier) -> MayRevert;
47
48 fn read_u32_selector(&self) -> MayRevert<u32>;
50
51 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 self.record_external_cost(None, Some(data_max_encoded_len as u64), None)
63 }
64
65 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 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 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 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 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 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}