aztec_pxe/sync/
event_filter.rs1use aztec_core::error::Error;
7use aztec_core::types::AztecAddress;
8
9use crate::stores::private_event_store::PrivateEventQueryFilter;
10
11use aztec_pxe_client::PrivateEventFilter;
12
13pub struct PrivateEventFilterValidator {
17 anchor_block_number: u64,
19}
20
21impl PrivateEventFilterValidator {
22 pub fn new(anchor_block_number: u64) -> Self {
23 Self {
24 anchor_block_number,
25 }
26 }
27
28 pub fn validate(&self, filter: &PrivateEventFilter) -> Result<PrivateEventQueryFilter, Error> {
37 if filter.scopes.is_empty() {
39 return Err(Error::InvalidData(
40 "at least one scope is required to get private events".into(),
41 ));
42 }
43
44 let from_block = filter.from_block.unwrap_or(1).max(1);
45
46 let to_block = filter.to_block.unwrap_or(self.anchor_block_number + 1);
48
49 if to_block > self.anchor_block_number + 1 {
51 return Err(Error::InvalidData(format!(
52 "to_block ({to_block}) exceeds anchor block + 1 ({})",
53 self.anchor_block_number + 1
54 )));
55 }
56
57 if from_block >= to_block {
58 return Err(Error::InvalidData(format!(
59 "invalid block range: from_block={from_block} >= to_block={to_block}"
60 )));
61 }
62
63 let inclusive_to_block = to_block - 1;
65
66 Ok(PrivateEventQueryFilter {
67 contract_address: filter.contract_address,
68 from_block: Some(from_block),
69 to_block: Some(inclusive_to_block),
70 scopes: filter.scopes.clone(),
71 tx_hash: filter.tx_hash,
72 })
73 }
74}
75
76pub fn to_query_filter_unchecked(filter: &PrivateEventFilter) -> PrivateEventQueryFilter {
79 PrivateEventQueryFilter {
80 contract_address: filter.contract_address,
81 from_block: filter.from_block,
82 to_block: filter.to_block,
83 scopes: filter.scopes.clone(),
84 tx_hash: filter.tx_hash,
85 }
86}
87
88pub fn all_events_filter(contract_address: AztecAddress) -> PrivateEventQueryFilter {
90 PrivateEventQueryFilter {
91 contract_address,
92 from_block: None,
93 to_block: None,
94 scopes: vec![],
95 tx_hash: None,
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 fn make_filter(from: Option<u64>, to: Option<u64>) -> PrivateEventFilter {
104 PrivateEventFilter {
105 contract_address: AztecAddress::from(1u64),
106 from_block: from,
107 to_block: to,
108 tx_hash: None,
109 after_log: None,
110 scopes: vec![AztecAddress::from(99u64)], }
112 }
113
114 #[test]
115 fn defaults_block_range_to_full() {
116 let validator = PrivateEventFilterValidator::new(10);
117 let result = validator.validate(&make_filter(None, None)).unwrap();
118 assert_eq!(result.from_block, Some(1));
119 assert_eq!(result.to_block, Some(10)); }
121
122 #[test]
123 fn rejects_to_block_beyond_anchor() {
124 let validator = PrivateEventFilterValidator::new(5);
125 let result = validator.validate(&make_filter(Some(1), Some(100)));
126 assert!(result.is_err(), "to_block beyond anchor should be rejected");
127 }
128
129 #[test]
130 fn clamps_from_block_to_at_least_1() {
131 let validator = PrivateEventFilterValidator::new(10);
132 let result = validator.validate(&make_filter(Some(0), None)).unwrap();
133 assert_eq!(result.from_block, Some(1));
134 }
135
136 #[test]
137 fn rejects_invalid_range() {
138 let validator = PrivateEventFilterValidator::new(5);
139 let result = validator.validate(&make_filter(Some(10), None));
141 assert!(result.is_err());
142 }
143
144 #[test]
145 fn rejects_empty_scopes() {
146 let validator = PrivateEventFilterValidator::new(10);
147 let filter = PrivateEventFilter {
148 contract_address: AztecAddress::from(1u64),
149 from_block: None,
150 to_block: None,
151 tx_hash: None,
152 after_log: None,
153 scopes: vec![],
154 };
155 let result = validator.validate(&filter);
156 assert!(result.is_err(), "empty scopes should be rejected");
157 }
158
159 #[test]
160 fn preserves_scopes() {
161 let validator = PrivateEventFilterValidator::new(10);
162 let mut filter = make_filter(None, None);
163 filter.scopes = vec![AztecAddress::from(42u64)];
164 let result = validator.validate(&filter).unwrap();
165 assert_eq!(result.scopes.len(), 1);
166 }
167
168 #[test]
169 fn to_query_filter_unchecked_passes_through() {
170 let filter = PrivateEventFilter {
171 contract_address: AztecAddress::from(1u64),
172 from_block: Some(3),
173 to_block: Some(7),
174 tx_hash: None,
175 after_log: None,
176 scopes: vec![AztecAddress::from(2u64)],
177 };
178 let query = to_query_filter_unchecked(&filter);
179 assert_eq!(query.from_block, Some(3));
180 assert_eq!(query.to_block, Some(7));
181 assert_eq!(query.scopes.len(), 1);
182 }
183}