1use crate::{hypersync_net_types_capnp, types::Sighash, BuilderReader, Selection};
2use hypersync_format::{Address, FilterWrapper};
3use serde::{Deserialize, Serialize};
4
5pub type TraceSelection = Selection<TraceFilter>;
6
7#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
8pub struct TraceFilter {
9 #[serde(default, skip_serializing_if = "Vec::is_empty")]
10 pub from: Vec<Address>,
11 #[serde(default, skip_serializing_if = "Option::is_none")]
12 pub from_filter: Option<FilterWrapper>,
13 #[serde(default, skip_serializing_if = "Vec::is_empty")]
14 pub to: Vec<Address>,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub to_filter: Option<FilterWrapper>,
17 #[serde(default, skip_serializing_if = "Vec::is_empty")]
18 pub address: Vec<Address>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub address_filter: Option<FilterWrapper>,
21 #[serde(default, skip_serializing_if = "Vec::is_empty")]
22 pub call_type: Vec<String>,
23 #[serde(default, skip_serializing_if = "Vec::is_empty")]
24 pub reward_type: Vec<String>,
25 #[serde(default, skip_serializing_if = "Vec::is_empty")]
26 #[serde(rename = "type")]
27 pub type_: Vec<String>,
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 pub sighash: Vec<Sighash>,
30}
31
32impl BuilderReader<hypersync_net_types_capnp::trace_filter::Owned> for TraceFilter {
33 fn populate_builder(
34 &self,
35 builder: &mut hypersync_net_types_capnp::trace_filter::Builder,
36 ) -> Result<(), capnp::Error> {
37 if !self.from.is_empty() {
39 let mut from_list = builder.reborrow().init_from(self.from.len() as u32);
40 for (i, addr) in self.from.iter().enumerate() {
41 from_list.set(i as u32, addr.as_slice());
42 }
43 }
44
45 if let Some(filter) = &self.from_filter {
47 builder.reborrow().set_from_filter(filter.0.as_bytes());
48 }
49
50 if !self.to.is_empty() {
52 let mut to_list = builder.reborrow().init_to(self.to.len() as u32);
53 for (i, addr) in self.to.iter().enumerate() {
54 to_list.set(i as u32, addr.as_slice());
55 }
56 }
57
58 if let Some(filter) = &self.to_filter {
60 builder.reborrow().set_to_filter(filter.0.as_bytes());
61 }
62
63 if !self.address.is_empty() {
65 let mut addr_list = builder.reborrow().init_address(self.address.len() as u32);
66 for (i, addr) in self.address.iter().enumerate() {
67 addr_list.set(i as u32, addr.as_slice());
68 }
69 }
70
71 if let Some(filter) = &self.address_filter {
73 builder.reborrow().set_address_filter(filter.0.as_bytes());
74 }
75
76 if !self.call_type.is_empty() {
78 let mut call_type_list = builder
79 .reborrow()
80 .init_call_type(self.call_type.len() as u32);
81 for (i, call_type) in self.call_type.iter().enumerate() {
82 call_type_list.set(i as u32, call_type);
83 }
84 }
85
86 if !self.reward_type.is_empty() {
88 let mut reward_type_list = builder
89 .reborrow()
90 .init_reward_type(self.reward_type.len() as u32);
91 for (i, reward_type) in self.reward_type.iter().enumerate() {
92 reward_type_list.set(i as u32, reward_type);
93 }
94 }
95
96 if !self.type_.is_empty() {
98 let mut type_list = builder.reborrow().init_type(self.type_.len() as u32);
99 for (i, type_) in self.type_.iter().enumerate() {
100 type_list.set(i as u32, type_);
101 }
102 }
103
104 if !self.sighash.is_empty() {
106 let mut sighash_list = builder.reborrow().init_sighash(self.sighash.len() as u32);
107 for (i, sighash) in self.sighash.iter().enumerate() {
108 sighash_list.set(i as u32, sighash.as_slice());
109 }
110 }
111
112 Ok(())
113 }
114
115 fn from_reader(
117 reader: hypersync_net_types_capnp::trace_filter::Reader,
118 ) -> Result<Self, capnp::Error> {
119 let mut from = Vec::new();
120
121 if reader.has_from() {
123 let from_list = reader.get_from()?;
124 for i in 0..from_list.len() {
125 let addr_data = from_list.get(i)?;
126 if addr_data.len() == 20 {
127 let mut addr_bytes = [0u8; 20];
128 addr_bytes.copy_from_slice(addr_data);
129 from.push(Address::from(addr_bytes));
130 }
131 }
132 }
133
134 let mut from_filter = None;
135
136 if reader.has_from_filter() {
138 let filter_data = reader.get_from_filter()?;
139 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
140 return Err(capnp::Error::failed("Invalid from filter".to_string()));
141 };
142 from_filter = Some(wrapper);
143 }
144
145 let mut to = Vec::new();
146
147 if reader.has_to() {
149 let to_list = reader.get_to()?;
150 for i in 0..to_list.len() {
151 let addr_data = to_list.get(i)?;
152 if addr_data.len() == 20 {
153 let mut addr_bytes = [0u8; 20];
154 addr_bytes.copy_from_slice(addr_data);
155 to.push(Address::from(addr_bytes));
156 }
157 }
158 }
159
160 let mut to_filter = None;
161
162 if reader.has_to_filter() {
164 let filter_data = reader.get_to_filter()?;
165 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
166 return Err(capnp::Error::failed("Invalid to filter".to_string()));
167 };
168 to_filter = Some(wrapper);
169 }
170
171 let mut address = Vec::new();
172
173 if reader.has_address() {
175 let addr_list = reader.get_address()?;
176 for i in 0..addr_list.len() {
177 let addr_data = addr_list.get(i)?;
178 if addr_data.len() == 20 {
179 let mut addr_bytes = [0u8; 20];
180 addr_bytes.copy_from_slice(addr_data);
181 address.push(Address::from(addr_bytes));
182 }
183 }
184 }
185
186 let mut address_filter = None;
187
188 if reader.has_address_filter() {
190 let filter_data = reader.get_address_filter()?;
191 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
192 return Err(capnp::Error::failed("Invalid address filter".to_string()));
193 };
194 address_filter = Some(wrapper);
195 }
196
197 let mut call_type = Vec::new();
198
199 if reader.has_call_type() {
201 let call_type_list = reader.get_call_type()?;
202 for i in 0..call_type_list.len() {
203 let call_type_val = call_type_list.get(i)?;
204 call_type.push(call_type_val.to_string()?);
205 }
206 }
207
208 let mut reward_type = Vec::new();
209 if reader.has_reward_type() {
211 let reward_type_list = reader.get_reward_type()?;
212 for i in 0..reward_type_list.len() {
213 let reward_type_val = reward_type_list.get(i)?;
214 reward_type.push(reward_type_val.to_string()?);
215 }
216 }
217
218 let mut type_ = Vec::new();
219
220 if reader.has_type() {
222 let type_list = reader.get_type()?;
223 for i in 0..type_list.len() {
224 let type_val = type_list.get(i)?;
225 type_.push(type_val.to_string()?);
226 }
227 }
228
229 let mut sighash = Vec::new();
230
231 if reader.has_sighash() {
233 let sighash_list = reader.get_sighash()?;
234 for i in 0..sighash_list.len() {
235 let sighash_data = sighash_list.get(i)?;
236 if sighash_data.len() == 4 {
237 let mut sighash_bytes = [0u8; 4];
238 sighash_bytes.copy_from_slice(sighash_data);
239 sighash.push(Sighash::from(sighash_bytes));
240 }
241 }
242 }
243
244 Ok(Self {
245 from,
246 from_filter,
247 to,
248 to_filter,
249 address,
250 address_filter,
251 call_type,
252 reward_type,
253 type_,
254 sighash,
255 })
256 }
257}
258
259#[derive(
260 Debug,
261 Clone,
262 Copy,
263 Serialize,
264 Deserialize,
265 PartialEq,
266 Eq,
267 schemars::JsonSchema,
268 strum_macros::EnumIter,
269 strum_macros::AsRefStr,
270 strum_macros::Display,
271 strum_macros::EnumString,
272)]
273#[serde(rename_all = "snake_case")]
274#[strum(serialize_all = "snake_case")]
275pub enum TraceField {
276 TransactionHash,
278 BlockHash,
279 BlockNumber,
280 TransactionPosition,
281 Type,
282 Error,
283
284 From,
286 To,
287 Author,
288
289 Gas,
291 GasUsed,
292
293 ActionAddress,
295 Address,
296 Balance,
297 CallType,
298 Code,
299 Init,
300 Input,
301 Output,
302 RefundAddress,
303 RewardType,
304 Sighash,
305 Subtraces,
306 TraceAddress,
307 Value,
308}
309
310impl Ord for TraceField {
311 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
312 self.as_ref().cmp(other.as_ref())
313 }
314}
315
316impl PartialOrd for TraceField {
317 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
318 Some(self.cmp(other))
319 }
320}
321
322impl TraceField {
323 pub fn all() -> std::collections::BTreeSet<Self> {
324 use strum::IntoEnumIterator;
325 Self::iter().collect()
326 }
327
328 pub fn to_capnp(&self) -> crate::hypersync_net_types_capnp::TraceField {
330 match self {
331 TraceField::TransactionHash => {
332 crate::hypersync_net_types_capnp::TraceField::TransactionHash
333 }
334 TraceField::BlockHash => crate::hypersync_net_types_capnp::TraceField::BlockHash,
335 TraceField::BlockNumber => crate::hypersync_net_types_capnp::TraceField::BlockNumber,
336 TraceField::TransactionPosition => {
337 crate::hypersync_net_types_capnp::TraceField::TransactionPosition
338 }
339 TraceField::Type => crate::hypersync_net_types_capnp::TraceField::Type,
340 TraceField::Error => crate::hypersync_net_types_capnp::TraceField::Error,
341 TraceField::From => crate::hypersync_net_types_capnp::TraceField::From,
342 TraceField::To => crate::hypersync_net_types_capnp::TraceField::To,
343 TraceField::Author => crate::hypersync_net_types_capnp::TraceField::Author,
344 TraceField::Gas => crate::hypersync_net_types_capnp::TraceField::Gas,
345 TraceField::GasUsed => crate::hypersync_net_types_capnp::TraceField::GasUsed,
346 TraceField::ActionAddress => {
347 crate::hypersync_net_types_capnp::TraceField::ActionAddress
348 }
349 TraceField::Address => crate::hypersync_net_types_capnp::TraceField::Address,
350 TraceField::Balance => crate::hypersync_net_types_capnp::TraceField::Balance,
351 TraceField::CallType => crate::hypersync_net_types_capnp::TraceField::CallType,
352 TraceField::Code => crate::hypersync_net_types_capnp::TraceField::Code,
353 TraceField::Init => crate::hypersync_net_types_capnp::TraceField::Init,
354 TraceField::Input => crate::hypersync_net_types_capnp::TraceField::Input,
355 TraceField::Output => crate::hypersync_net_types_capnp::TraceField::Output,
356 TraceField::RefundAddress => {
357 crate::hypersync_net_types_capnp::TraceField::RefundAddress
358 }
359 TraceField::RewardType => crate::hypersync_net_types_capnp::TraceField::RewardType,
360 TraceField::Sighash => crate::hypersync_net_types_capnp::TraceField::Sighash,
361 TraceField::Subtraces => crate::hypersync_net_types_capnp::TraceField::Subtraces,
362 TraceField::TraceAddress => crate::hypersync_net_types_capnp::TraceField::TraceAddress,
363 TraceField::Value => crate::hypersync_net_types_capnp::TraceField::Value,
364 }
365 }
366
367 pub fn from_capnp(field: crate::hypersync_net_types_capnp::TraceField) -> Self {
369 match field {
370 crate::hypersync_net_types_capnp::TraceField::TransactionHash => {
371 TraceField::TransactionHash
372 }
373 crate::hypersync_net_types_capnp::TraceField::BlockHash => TraceField::BlockHash,
374 crate::hypersync_net_types_capnp::TraceField::BlockNumber => TraceField::BlockNumber,
375 crate::hypersync_net_types_capnp::TraceField::TransactionPosition => {
376 TraceField::TransactionPosition
377 }
378 crate::hypersync_net_types_capnp::TraceField::Type => TraceField::Type,
379 crate::hypersync_net_types_capnp::TraceField::Error => TraceField::Error,
380 crate::hypersync_net_types_capnp::TraceField::From => TraceField::From,
381 crate::hypersync_net_types_capnp::TraceField::To => TraceField::To,
382 crate::hypersync_net_types_capnp::TraceField::Author => TraceField::Author,
383 crate::hypersync_net_types_capnp::TraceField::Gas => TraceField::Gas,
384 crate::hypersync_net_types_capnp::TraceField::GasUsed => TraceField::GasUsed,
385 crate::hypersync_net_types_capnp::TraceField::ActionAddress => {
386 TraceField::ActionAddress
387 }
388 crate::hypersync_net_types_capnp::TraceField::Address => TraceField::Address,
389 crate::hypersync_net_types_capnp::TraceField::Balance => TraceField::Balance,
390 crate::hypersync_net_types_capnp::TraceField::CallType => TraceField::CallType,
391 crate::hypersync_net_types_capnp::TraceField::Code => TraceField::Code,
392 crate::hypersync_net_types_capnp::TraceField::Init => TraceField::Init,
393 crate::hypersync_net_types_capnp::TraceField::Input => TraceField::Input,
394 crate::hypersync_net_types_capnp::TraceField::Output => TraceField::Output,
395 crate::hypersync_net_types_capnp::TraceField::RefundAddress => {
396 TraceField::RefundAddress
397 }
398 crate::hypersync_net_types_capnp::TraceField::RewardType => TraceField::RewardType,
399 crate::hypersync_net_types_capnp::TraceField::Sighash => TraceField::Sighash,
400 crate::hypersync_net_types_capnp::TraceField::Subtraces => TraceField::Subtraces,
401 crate::hypersync_net_types_capnp::TraceField::TraceAddress => TraceField::TraceAddress,
402 crate::hypersync_net_types_capnp::TraceField::Value => TraceField::Value,
403 }
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use hypersync_format::Hex;
410
411 use super::*;
412 use crate::{query::tests::test_query_serde, FieldSelection, Query};
413
414 #[test]
415 fn test_all_fields_in_schema() {
416 let schema = hypersync_schema::trace();
417 let schema_fields = schema
418 .fields
419 .iter()
420 .map(|f| f.name.clone())
421 .collect::<std::collections::BTreeSet<_>>();
422 let all_fields = TraceField::all()
423 .into_iter()
424 .map(|f| f.as_ref().to_string())
425 .collect::<std::collections::BTreeSet<_>>();
426 assert_eq!(schema_fields, all_fields);
427 }
428
429 #[test]
430 fn test_serde_matches_strum() {
431 for field in TraceField::all() {
432 let serialized = serde_json::to_string(&field).unwrap();
433 let strum = serde_json::to_string(&field.as_ref()).unwrap();
434 assert_eq!(serialized, strum, "strum value should be the same as serde");
435 }
436 }
437
438 #[test]
439 fn test_trace_filter_serde_with_defaults() {
440 let trace_filter = TraceSelection::default();
441 let field_selection = FieldSelection {
442 trace: TraceField::all(),
443 ..Default::default()
444 };
445 let query = Query {
446 traces: vec![trace_filter],
447 field_selection,
448 ..Default::default()
449 };
450
451 test_query_serde(query, "trace selection with defaults");
452 }
453
454 #[test]
455 fn test_trace_filter_serde_with_full_values() {
456 let trace_filter = TraceFilter {
457 from: vec![Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap()],
458 from_filter: Some(FilterWrapper::new(16, 1)),
459 to: vec![Address::decode_hex("0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6").unwrap()],
460 to_filter: Some(FilterWrapper::new(16, 1)),
461 address: vec![
462 Address::decode_hex("0x1234567890123456789012345678901234567890").unwrap(),
463 ],
464 address_filter: Some(FilterWrapper::new(16, 1)),
465 call_type: vec!["call".to_string(), "create".to_string()],
466 reward_type: vec!["block".to_string(), "uncle".to_string()],
467 type_: vec!["call".to_string(), "create".to_string()],
468 sighash: vec![Sighash::from([0x12, 0x34, 0x56, 0x78])],
469 };
470 let field_selection = FieldSelection {
471 trace: TraceField::all(),
472 ..Default::default()
473 };
474 let query = Query {
475 traces: vec![trace_filter.into()],
476 field_selection,
477 ..Default::default()
478 };
479
480 test_query_serde(query, "trace selection with full values");
481 }
482}