1use crate::{
2 hypersync_net_types_capnp,
3 types::{AnyOf, Sighash},
4 CapnpBuilder, CapnpReader, Selection,
5};
6use anyhow::Context;
7use hypersync_format::{Address, FilterWrapper};
8use serde::{Deserialize, Serialize};
9
10pub type TraceSelection = Selection<TraceFilter>;
11
12impl From<TraceFilter> for AnyOf<TraceFilter> {
13 fn from(filter: TraceFilter) -> Self {
14 Self::new(filter)
15 }
16}
17
18#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq)]
19pub struct TraceFilter {
20 #[serde(default, skip_serializing_if = "Vec::is_empty")]
21 pub from: Vec<Address>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub from_filter: Option<FilterWrapper>,
24 #[serde(default, skip_serializing_if = "Vec::is_empty")]
25 pub to: Vec<Address>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub to_filter: Option<FilterWrapper>,
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 pub address: Vec<Address>,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub address_filter: Option<FilterWrapper>,
32 #[serde(default, skip_serializing_if = "Vec::is_empty")]
33 pub call_type: Vec<String>,
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
35 pub reward_type: Vec<String>,
36 #[serde(default, skip_serializing_if = "Vec::is_empty")]
37 #[serde(rename = "type")]
38 pub type_: Vec<String>,
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 pub sighash: Vec<Sighash>,
41}
42
43impl TraceFilter {
44 pub fn all() -> Self {
63 Default::default()
64 }
65
66 pub fn or(self, other: Self) -> AnyOf<Self> {
92 AnyOf::new(self).or(other)
93 }
94
95 pub fn and_from<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
134 where
135 I: IntoIterator<Item = A>,
136 A: TryInto<Address>,
137 A::Error: std::error::Error + Send + Sync + 'static,
138 {
139 let mut converted_addresses: Vec<Address> = Vec::new();
140 for (idx, address) in addresses.into_iter().enumerate() {
141 converted_addresses.push(
142 address
143 .try_into()
144 .with_context(|| format!("invalid from address at position {idx}"))?,
145 );
146 }
147 self.from = converted_addresses;
148 Ok(self)
149 }
150
151 pub fn and_to<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
187 where
188 I: IntoIterator<Item = A>,
189 A: TryInto<Address>,
190 A::Error: std::error::Error + Send + Sync + 'static,
191 {
192 let mut converted_addresses: Vec<Address> = Vec::new();
193 for (idx, address) in addresses.into_iter().enumerate() {
194 converted_addresses.push(
195 address
196 .try_into()
197 .with_context(|| format!("invalid to address at position {idx}"))?,
198 );
199 }
200 self.to = converted_addresses;
201 Ok(self)
202 }
203
204 pub fn and_address<I, A>(mut self, addresses: I) -> anyhow::Result<Self>
235 where
236 I: IntoIterator<Item = A>,
237 A: TryInto<Address>,
238 A::Error: std::error::Error + Send + Sync + 'static,
239 {
240 let mut converted_addresses: Vec<Address> = Vec::new();
241 for (idx, address) in addresses.into_iter().enumerate() {
242 converted_addresses.push(
243 address
244 .try_into()
245 .with_context(|| format!("invalid address at position {idx}"))?,
246 );
247 }
248 self.address = converted_addresses;
249 Ok(self)
250 }
251
252 pub fn and_call_type<I, S>(mut self, call_types: I) -> Self
280 where
281 I: IntoIterator<Item = S>,
282 S: Into<String>,
283 {
284 self.call_type = call_types.into_iter().map(Into::into).collect();
285 self
286 }
287
288 pub fn and_reward_type<I, S>(mut self, reward_types: I) -> Self
310 where
311 I: IntoIterator<Item = S>,
312 S: Into<String>,
313 {
314 self.reward_type = reward_types.into_iter().map(Into::into).collect();
315 self
316 }
317
318 pub fn and_type<I, S>(mut self, types: I) -> Self
340 where
341 I: IntoIterator<Item = S>,
342 S: Into<String>,
343 {
344 self.type_ = types.into_iter().map(Into::into).collect();
345 self
346 }
347
348 pub fn and_sighash<I, S>(mut self, sighashes: I) -> anyhow::Result<Self>
385 where
386 I: IntoIterator<Item = S>,
387 S: TryInto<Sighash>,
388 S::Error: std::error::Error + Send + Sync + 'static,
389 {
390 let mut converted_sighashes: Vec<Sighash> = Vec::new();
391 for (idx, sighash) in sighashes.into_iter().enumerate() {
392 converted_sighashes.push(
393 sighash
394 .try_into()
395 .with_context(|| format!("invalid sighash at position {idx}"))?,
396 );
397 }
398 self.sighash = converted_sighashes;
399 Ok(self)
400 }
401}
402
403impl CapnpBuilder<hypersync_net_types_capnp::trace_filter::Owned> for TraceFilter {
404 fn populate_builder(
405 &self,
406 builder: &mut hypersync_net_types_capnp::trace_filter::Builder,
407 ) -> Result<(), capnp::Error> {
408 if !self.from.is_empty() {
410 let mut from_list = builder.reborrow().init_from(self.from.len() as u32);
411 for (i, addr) in self.from.iter().enumerate() {
412 from_list.set(i as u32, addr.as_slice());
413 }
414 }
415
416 if let Some(filter) = &self.from_filter {
418 builder.reborrow().set_from_filter(filter.0.as_bytes());
419 }
420
421 if !self.to.is_empty() {
423 let mut to_list = builder.reborrow().init_to(self.to.len() as u32);
424 for (i, addr) in self.to.iter().enumerate() {
425 to_list.set(i as u32, addr.as_slice());
426 }
427 }
428
429 if let Some(filter) = &self.to_filter {
431 builder.reborrow().set_to_filter(filter.0.as_bytes());
432 }
433
434 if !self.address.is_empty() {
436 let mut addr_list = builder.reborrow().init_address(self.address.len() as u32);
437 for (i, addr) in self.address.iter().enumerate() {
438 addr_list.set(i as u32, addr.as_slice());
439 }
440 }
441
442 if let Some(filter) = &self.address_filter {
444 builder.reborrow().set_address_filter(filter.0.as_bytes());
445 }
446
447 if !self.call_type.is_empty() {
449 let mut call_type_list = builder
450 .reborrow()
451 .init_call_type(self.call_type.len() as u32);
452 for (i, call_type) in self.call_type.iter().enumerate() {
453 call_type_list.set(i as u32, call_type);
454 }
455 }
456
457 if !self.reward_type.is_empty() {
459 let mut reward_type_list = builder
460 .reborrow()
461 .init_reward_type(self.reward_type.len() as u32);
462 for (i, reward_type) in self.reward_type.iter().enumerate() {
463 reward_type_list.set(i as u32, reward_type);
464 }
465 }
466
467 if !self.type_.is_empty() {
469 let mut type_list = builder.reborrow().init_type(self.type_.len() as u32);
470 for (i, type_) in self.type_.iter().enumerate() {
471 type_list.set(i as u32, type_);
472 }
473 }
474
475 if !self.sighash.is_empty() {
477 let mut sighash_list = builder.reborrow().init_sighash(self.sighash.len() as u32);
478 for (i, sighash) in self.sighash.iter().enumerate() {
479 sighash_list.set(i as u32, sighash.as_slice());
480 }
481 }
482
483 Ok(())
484 }
485}
486
487impl CapnpReader<hypersync_net_types_capnp::trace_filter::Owned> for TraceFilter {
488 fn from_reader(
490 reader: hypersync_net_types_capnp::trace_filter::Reader,
491 ) -> Result<Self, capnp::Error> {
492 let mut from = Vec::new();
493
494 if reader.has_from() {
496 let from_list = reader.get_from()?;
497 for i in 0..from_list.len() {
498 let addr_data = from_list.get(i)?;
499 if addr_data.len() == 20 {
500 let mut addr_bytes = [0u8; 20];
501 addr_bytes.copy_from_slice(addr_data);
502 from.push(Address::from(addr_bytes));
503 }
504 }
505 }
506
507 let mut from_filter = None;
508
509 if reader.has_from_filter() {
511 let filter_data = reader.get_from_filter()?;
512 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
513 return Err(capnp::Error::failed("Invalid from filter".to_string()));
514 };
515 from_filter = Some(wrapper);
516 }
517
518 let mut to = Vec::new();
519
520 if reader.has_to() {
522 let to_list = reader.get_to()?;
523 for i in 0..to_list.len() {
524 let addr_data = to_list.get(i)?;
525 if addr_data.len() == 20 {
526 let mut addr_bytes = [0u8; 20];
527 addr_bytes.copy_from_slice(addr_data);
528 to.push(Address::from(addr_bytes));
529 }
530 }
531 }
532
533 let mut to_filter = None;
534
535 if reader.has_to_filter() {
537 let filter_data = reader.get_to_filter()?;
538 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
539 return Err(capnp::Error::failed("Invalid to filter".to_string()));
540 };
541 to_filter = Some(wrapper);
542 }
543
544 let mut address = Vec::new();
545
546 if reader.has_address() {
548 let addr_list = reader.get_address()?;
549 for i in 0..addr_list.len() {
550 let addr_data = addr_list.get(i)?;
551 if addr_data.len() == 20 {
552 let mut addr_bytes = [0u8; 20];
553 addr_bytes.copy_from_slice(addr_data);
554 address.push(Address::from(addr_bytes));
555 }
556 }
557 }
558
559 let mut address_filter = None;
560
561 if reader.has_address_filter() {
563 let filter_data = reader.get_address_filter()?;
564 let Ok(wrapper) = FilterWrapper::from_bytes(filter_data) else {
565 return Err(capnp::Error::failed("Invalid address filter".to_string()));
566 };
567 address_filter = Some(wrapper);
568 }
569
570 let mut call_type = Vec::new();
571
572 if reader.has_call_type() {
574 let call_type_list = reader.get_call_type()?;
575 for i in 0..call_type_list.len() {
576 let call_type_val = call_type_list.get(i)?;
577 call_type.push(call_type_val.to_string()?);
578 }
579 }
580
581 let mut reward_type = Vec::new();
582 if reader.has_reward_type() {
584 let reward_type_list = reader.get_reward_type()?;
585 for i in 0..reward_type_list.len() {
586 let reward_type_val = reward_type_list.get(i)?;
587 reward_type.push(reward_type_val.to_string()?);
588 }
589 }
590
591 let mut type_ = Vec::new();
592
593 if reader.has_type() {
595 let type_list = reader.get_type()?;
596 for i in 0..type_list.len() {
597 let type_val = type_list.get(i)?;
598 type_.push(type_val.to_string()?);
599 }
600 }
601
602 let mut sighash = Vec::new();
603
604 if reader.has_sighash() {
606 let sighash_list = reader.get_sighash()?;
607 for i in 0..sighash_list.len() {
608 let sighash_data = sighash_list.get(i)?;
609 if sighash_data.len() == 4 {
610 let mut sighash_bytes = [0u8; 4];
611 sighash_bytes.copy_from_slice(sighash_data);
612 sighash.push(Sighash::from(sighash_bytes));
613 }
614 }
615 }
616
617 Ok(Self {
618 from,
619 from_filter,
620 to,
621 to_filter,
622 address,
623 address_filter,
624 call_type,
625 reward_type,
626 type_,
627 sighash,
628 })
629 }
630}
631
632#[derive(
633 Debug,
634 Clone,
635 Copy,
636 Serialize,
637 Deserialize,
638 PartialEq,
639 Eq,
640 schemars::JsonSchema,
641 strum_macros::EnumIter,
642 strum_macros::AsRefStr,
643 strum_macros::Display,
644 strum_macros::EnumString,
645)]
646#[serde(rename_all = "snake_case")]
647#[strum(serialize_all = "snake_case")]
648pub enum TraceField {
649 TransactionHash,
651 BlockHash,
652 BlockNumber,
653 TransactionPosition,
654 Type,
655 Error,
656
657 From,
659 To,
660 Author,
661
662 Gas,
664 GasUsed,
665
666 ActionAddress,
668 Address,
669 Balance,
670 CallType,
671 Code,
672 Init,
673 Input,
674 Output,
675 RefundAddress,
676 RewardType,
677 Sighash,
678 Subtraces,
679 TraceAddress,
680 Value,
681}
682
683impl Ord for TraceField {
684 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
685 self.as_ref().cmp(other.as_ref())
686 }
687}
688
689impl PartialOrd for TraceField {
690 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
691 Some(self.cmp(other))
692 }
693}
694
695impl TraceField {
696 pub fn all() -> std::collections::BTreeSet<Self> {
697 use strum::IntoEnumIterator;
698 Self::iter().collect()
699 }
700
701 pub fn to_capnp(&self) -> crate::hypersync_net_types_capnp::TraceField {
703 match self {
704 TraceField::TransactionHash => {
705 crate::hypersync_net_types_capnp::TraceField::TransactionHash
706 }
707 TraceField::BlockHash => crate::hypersync_net_types_capnp::TraceField::BlockHash,
708 TraceField::BlockNumber => crate::hypersync_net_types_capnp::TraceField::BlockNumber,
709 TraceField::TransactionPosition => {
710 crate::hypersync_net_types_capnp::TraceField::TransactionPosition
711 }
712 TraceField::Type => crate::hypersync_net_types_capnp::TraceField::Type,
713 TraceField::Error => crate::hypersync_net_types_capnp::TraceField::Error,
714 TraceField::From => crate::hypersync_net_types_capnp::TraceField::From,
715 TraceField::To => crate::hypersync_net_types_capnp::TraceField::To,
716 TraceField::Author => crate::hypersync_net_types_capnp::TraceField::Author,
717 TraceField::Gas => crate::hypersync_net_types_capnp::TraceField::Gas,
718 TraceField::GasUsed => crate::hypersync_net_types_capnp::TraceField::GasUsed,
719 TraceField::ActionAddress => {
720 crate::hypersync_net_types_capnp::TraceField::ActionAddress
721 }
722 TraceField::Address => crate::hypersync_net_types_capnp::TraceField::Address,
723 TraceField::Balance => crate::hypersync_net_types_capnp::TraceField::Balance,
724 TraceField::CallType => crate::hypersync_net_types_capnp::TraceField::CallType,
725 TraceField::Code => crate::hypersync_net_types_capnp::TraceField::Code,
726 TraceField::Init => crate::hypersync_net_types_capnp::TraceField::Init,
727 TraceField::Input => crate::hypersync_net_types_capnp::TraceField::Input,
728 TraceField::Output => crate::hypersync_net_types_capnp::TraceField::Output,
729 TraceField::RefundAddress => {
730 crate::hypersync_net_types_capnp::TraceField::RefundAddress
731 }
732 TraceField::RewardType => crate::hypersync_net_types_capnp::TraceField::RewardType,
733 TraceField::Sighash => crate::hypersync_net_types_capnp::TraceField::Sighash,
734 TraceField::Subtraces => crate::hypersync_net_types_capnp::TraceField::Subtraces,
735 TraceField::TraceAddress => crate::hypersync_net_types_capnp::TraceField::TraceAddress,
736 TraceField::Value => crate::hypersync_net_types_capnp::TraceField::Value,
737 }
738 }
739
740 pub fn from_capnp(field: crate::hypersync_net_types_capnp::TraceField) -> Self {
742 match field {
743 crate::hypersync_net_types_capnp::TraceField::TransactionHash => {
744 TraceField::TransactionHash
745 }
746 crate::hypersync_net_types_capnp::TraceField::BlockHash => TraceField::BlockHash,
747 crate::hypersync_net_types_capnp::TraceField::BlockNumber => TraceField::BlockNumber,
748 crate::hypersync_net_types_capnp::TraceField::TransactionPosition => {
749 TraceField::TransactionPosition
750 }
751 crate::hypersync_net_types_capnp::TraceField::Type => TraceField::Type,
752 crate::hypersync_net_types_capnp::TraceField::Error => TraceField::Error,
753 crate::hypersync_net_types_capnp::TraceField::From => TraceField::From,
754 crate::hypersync_net_types_capnp::TraceField::To => TraceField::To,
755 crate::hypersync_net_types_capnp::TraceField::Author => TraceField::Author,
756 crate::hypersync_net_types_capnp::TraceField::Gas => TraceField::Gas,
757 crate::hypersync_net_types_capnp::TraceField::GasUsed => TraceField::GasUsed,
758 crate::hypersync_net_types_capnp::TraceField::ActionAddress => {
759 TraceField::ActionAddress
760 }
761 crate::hypersync_net_types_capnp::TraceField::Address => TraceField::Address,
762 crate::hypersync_net_types_capnp::TraceField::Balance => TraceField::Balance,
763 crate::hypersync_net_types_capnp::TraceField::CallType => TraceField::CallType,
764 crate::hypersync_net_types_capnp::TraceField::Code => TraceField::Code,
765 crate::hypersync_net_types_capnp::TraceField::Init => TraceField::Init,
766 crate::hypersync_net_types_capnp::TraceField::Input => TraceField::Input,
767 crate::hypersync_net_types_capnp::TraceField::Output => TraceField::Output,
768 crate::hypersync_net_types_capnp::TraceField::RefundAddress => {
769 TraceField::RefundAddress
770 }
771 crate::hypersync_net_types_capnp::TraceField::RewardType => TraceField::RewardType,
772 crate::hypersync_net_types_capnp::TraceField::Sighash => TraceField::Sighash,
773 crate::hypersync_net_types_capnp::TraceField::Subtraces => TraceField::Subtraces,
774 crate::hypersync_net_types_capnp::TraceField::TraceAddress => TraceField::TraceAddress,
775 crate::hypersync_net_types_capnp::TraceField::Value => TraceField::Value,
776 }
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use hypersync_format::Hex;
783
784 use super::*;
785 use crate::{query::tests::test_query_serde, Query};
786
787 #[test]
788 fn test_all_fields_in_schema() {
789 let schema = hypersync_schema::trace();
790 let schema_fields = schema
791 .fields
792 .iter()
793 .map(|f| f.name.clone())
794 .collect::<std::collections::BTreeSet<_>>();
795 let all_fields = TraceField::all()
796 .into_iter()
797 .map(|f| f.as_ref().to_string())
798 .collect::<std::collections::BTreeSet<_>>();
799 assert_eq!(schema_fields, all_fields);
800 }
801
802 #[test]
803 fn test_serde_matches_strum() {
804 for field in TraceField::all() {
805 let serialized = serde_json::to_string(&field).unwrap();
806 let strum = serde_json::to_string(&field.as_ref()).unwrap();
807 assert_eq!(serialized, strum, "strum value should be the same as serde");
808 }
809 }
810
811 #[test]
812 fn test_trace_filter_serde_with_defaults() {
813 let trace_filter = TraceSelection::default();
814 let query = Query::new()
815 .where_traces(trace_filter)
816 .select_trace_fields(TraceField::all());
817
818 test_query_serde(query, "trace selection with defaults");
819 }
820
821 #[test]
822 fn test_trace_filter_serde_with_full_values() {
823 let trace_filter = TraceFilter {
824 from: vec![Address::decode_hex("0xdadB0d80178819F2319190D340ce9A924f783711").unwrap()],
825 from_filter: Some(FilterWrapper::new(16, 1)),
826 to: vec![Address::decode_hex("0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6").unwrap()],
827 to_filter: Some(FilterWrapper::new(16, 1)),
828 address: vec![
829 Address::decode_hex("0x1234567890123456789012345678901234567890").unwrap(),
830 ],
831 address_filter: Some(FilterWrapper::new(16, 1)),
832 call_type: vec!["call".to_string(), "create".to_string()],
833 reward_type: vec!["block".to_string(), "uncle".to_string()],
834 type_: vec!["call".to_string(), "create".to_string()],
835 sighash: vec![Sighash::from([0x12, 0x34, 0x56, 0x78])],
836 };
837 let query = Query::new()
838 .where_traces(trace_filter)
839 .select_trace_fields(TraceField::all());
840
841 test_query_serde(query, "trace selection with full values");
842 }
843}