1mod data;
18
19use ethereum::Log;
20use evm::{executor::stack::PrecompileFailure, ExitError};
21use primitive_types::{H160, H256};
22
23pub use data::{Address, EvmData, EvmDataReader, EvmDataWriter};
24pub use evm_precompiles_derive::generate_function_selector;
25
26pub type EvmResult<T = ()> = Result<T, PrecompileFailure>;
28
29#[macro_export(crate)]
30macro_rules! err {
31 ($e: expr) => {
32 PrecompileFailure::Error { exit_status: $e }
33 };
34}
35
36pub fn error<T: Into<std::borrow::Cow<'static, str>>>(text: T) -> PrecompileFailure {
38 err!(ExitError::Other(text.into()))
39}
40
41#[derive(Clone, Debug)]
43pub struct LogsBuilder {
44 address: H160,
45 logs: Vec<Log>,
46}
47
48impl LogsBuilder {
49 pub fn new(address: H160) -> Self {
52 Self {
53 logs: vec![],
54 address,
55 }
56 }
57
58 pub fn build(self) -> Vec<Log> {
60 self.logs
61 }
62
63 pub fn log0<D>(mut self, data: D) -> Self
65 where
66 D: Into<Vec<u8>>,
67 {
68 self.logs.push(Log {
69 address: self.address,
70 data: data.into(),
71 topics: vec![],
72 });
73 self
74 }
75
76 pub fn log1<D, T0>(mut self, topic0: T0, data: D) -> Self
78 where
79 D: Into<Vec<u8>>,
80 T0: Into<H256>,
81 {
82 self.logs.push(Log {
83 address: self.address,
84 data: data.into(),
85 topics: vec![topic0.into()],
86 });
87 self
88 }
89
90 pub fn log2<D, T0, T1>(mut self, topic0: T0, topic1: T1, data: D) -> Self
92 where
93 D: Into<Vec<u8>>,
94 T0: Into<H256>,
95 T1: Into<H256>,
96 {
97 self.logs.push(Log {
98 address: self.address,
99 data: data.into(),
100 topics: vec![topic0.into(), topic1.into()],
101 });
102 self
103 }
104
105 pub fn log3<D, T0, T1, T2>(mut self, topic0: T0, topic1: T1, topic2: T2, data: D) -> Self
107 where
108 D: Into<Vec<u8>>,
109 T0: Into<H256>,
110 T1: Into<H256>,
111 T2: Into<H256>,
112 {
113 self.logs.push(Log {
114 address: self.address,
115 data: data.into(),
116 topics: vec![topic0.into(), topic1.into(), topic2.into()],
117 });
118 self
119 }
120
121 pub fn log4<D, T0, T1, T2, T3>(
123 mut self,
124 topic0: T0,
125 topic1: T1,
126 topic2: T2,
127 topic3: T3,
128 data: D,
129 ) -> Self
130 where
131 D: Into<Vec<u8>>,
132 T0: Into<H256>,
133 T1: Into<H256>,
134 T2: Into<H256>,
135 T3: Into<H256>,
136 {
137 self.logs.push(Log {
138 address: self.address,
139 data: data.into(),
140 topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()],
141 });
142 self
143 }
144}
145
146#[derive(Clone, Copy, Debug)]
150pub struct Gasometer {
151 target_gas: Option<u64>,
152 used_gas: u64,
153}
154
155impl Gasometer {
156 pub fn new(target_gas: Option<u64>) -> Self {
159 Self {
160 target_gas,
161 used_gas: 0,
162 }
163 }
164
165 pub fn used_gas(&self) -> u64 {
167 self.used_gas
168 }
169
170 pub fn record_cost(&mut self, cost: u64) -> EvmResult {
172 self.used_gas = self
173 .used_gas
174 .checked_add(cost)
175 .ok_or(err!(ExitError::OutOfGas))?;
176
177 match self.target_gas {
178 Some(gas_limit) if self.used_gas > gas_limit => Err(err!(ExitError::OutOfGas)),
179 _ => Ok(()),
180 }
181 }
182
183 pub fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult {
186 const G_LOG: u64 = 375;
190 const G_LOGDATA: u64 = 8;
191 const G_LOGTOPIC: u64 = 375;
192
193 let topic_cost = G_LOGTOPIC
194 .checked_mul(topics as u64)
195 .ok_or(err!(ExitError::OutOfGas))?;
196
197 let data_cost = G_LOGDATA
198 .checked_mul(data_len as u64)
199 .ok_or(err!(ExitError::OutOfGas))?;
200
201 self.record_cost(G_LOG)?;
202 self.record_cost(topic_cost)?;
203 self.record_cost(data_cost)?;
204
205 Ok(())
206 }
207
208 pub fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult {
210 for log in logs {
211 self.record_log_costs_manual(log.topics.len(), log.data.len())?;
212 }
213
214 Ok(())
215 }
216
217 pub fn remaining_gas(&self) -> EvmResult<Option<u64>> {
221 Ok(match self.target_gas {
222 None => None,
223 Some(gas_limit) => Some(
224 gas_limit
225 .checked_sub(self.used_gas)
226 .ok_or(err!(ExitError::OutOfGas))?,
227 ),
228 })
229 }
230}