precompile_utils/testing/
handle.rs1use crate::testing::PrettyLog;
20use alloc::boxed::Box;
21use evm::{ExitRevert, ExitSucceed};
22use fp_evm::{Context, ExitError, ExitReason, Log, PrecompileHandle, Transfer};
23use sp_core::{H160, H256};
24
25use super::Alice;
26
27#[derive(Debug, Clone)]
28pub struct Subcall {
29 pub address: H160,
30 pub transfer: Option<Transfer>,
31 pub input: Vec<u8>,
32 pub target_gas: Option<u64>,
33 pub is_static: bool,
34 pub context: Context,
35}
36
37#[derive(Debug, Clone)]
38pub struct SubcallOutput {
39 pub reason: ExitReason,
40 pub output: Vec<u8>,
41 pub cost: u64,
42 pub logs: Vec<Log>,
43}
44
45impl SubcallOutput {
46 pub fn revert() -> Self {
47 Self {
48 reason: ExitReason::Revert(ExitRevert::Reverted),
49 output: Vec::new(),
50 cost: 0,
51 logs: Vec::new(),
52 }
53 }
54
55 pub fn succeed() -> Self {
56 Self {
57 reason: ExitReason::Succeed(ExitSucceed::Returned),
58 output: Vec::new(),
59 cost: 0,
60 logs: Vec::new(),
61 }
62 }
63
64 pub fn out_of_gas() -> Self {
65 Self {
66 reason: ExitReason::Error(ExitError::OutOfGas),
67 output: Vec::new(),
68 cost: 0,
69 logs: Vec::new(),
70 }
71 }
72}
73
74pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {}
75
76impl<T: FnMut(Subcall) -> SubcallOutput + 'static> SubcallTrait for T {}
77
78pub type SubcallHandle = Box<dyn SubcallTrait>;
79
80pub struct MockHandle {
82 pub gas_limit: u64,
83 pub gas_used: u64,
84 pub logs: Vec<PrettyLog>,
85 pub subcall_handle: Option<SubcallHandle>,
86 pub code_address: H160,
87 pub input: Vec<u8>,
88 pub context: Context,
89 pub is_static: bool,
90}
91
92impl MockHandle {
93 pub fn new(code_address: H160, context: Context) -> Self {
94 Self {
95 gas_limit: u64::MAX,
96 gas_used: 0,
97 logs: vec![],
98 subcall_handle: None,
99 code_address,
100 input: Vec::new(),
101 context,
102 is_static: false,
103 }
104 }
105}
106
107impl PrecompileHandle for MockHandle {
108 fn call(
111 &mut self,
112 address: H160,
113 transfer: Option<Transfer>,
114 input: Vec<u8>,
115 target_gas: Option<u64>,
116 is_static: bool,
117 context: &Context,
118 ) -> (ExitReason, Vec<u8>) {
119 if self
120 .record_cost(crate::evm::costs::call_cost(
121 context.apparent_value,
122 &evm::Config::pectra(),
123 ))
124 .is_err()
125 {
126 return (ExitReason::Error(ExitError::OutOfGas), vec![]);
127 }
128
129 match &mut self.subcall_handle {
130 Some(handle) => {
131 let SubcallOutput {
132 reason,
133 output,
134 cost,
135 logs,
136 } = handle(Subcall {
137 address,
138 transfer,
139 input,
140 target_gas,
141 is_static,
142 context: context.clone(),
143 });
144
145 if self.record_cost(cost).is_err() {
146 return (ExitReason::Error(ExitError::OutOfGas), vec![]);
147 }
148
149 for log in logs {
150 self.log(log.address, log.topics, log.data)
151 .expect("cannot fail");
152 }
153
154 (reason, output)
155 }
156 None => panic!("no subcall handle registered"),
157 }
158 }
159
160 fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> {
161 self.gas_used += cost;
162
163 if self.gas_used > self.gas_limit {
164 Err(ExitError::OutOfGas)
165 } else {
166 Ok(())
167 }
168 }
169
170 fn remaining_gas(&self) -> u64 {
171 self.gas_limit - self.gas_used
172 }
173
174 fn log(&mut self, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError> {
175 self.logs.push(PrettyLog(Log {
176 address,
177 topics,
178 data,
179 }));
180 Ok(())
181 }
182
183 fn code_address(&self) -> H160 {
185 self.code_address
186 }
187
188 fn input(&self) -> &[u8] {
190 &self.input
191 }
192
193 fn context(&self) -> &Context {
195 &self.context
196 }
197
198 fn is_static(&self) -> bool {
200 self.is_static
201 }
202
203 fn gas_limit(&self) -> Option<u64> {
205 Some(self.gas_limit)
206 }
207
208 fn record_external_cost(
209 &mut self,
210 _ref_time: Option<u64>,
211 _proof_size: Option<u64>,
212 _storage_growth: Option<u64>,
213 ) -> Result<(), ExitError> {
214 Ok(())
215 }
216
217 fn refund_external_cost(&mut self, _ref_time: Option<u64>, _proof_size: Option<u64>) {}
218
219 fn origin(&self) -> H160 {
220 Alice.into()
221 }
222
223 fn is_contract_being_constructed(&self, _address: H160) -> bool {
224 false
225 }
226}