hotshot_testing/predicates/
event.rs1use std::sync::Arc;
8
9use async_lock::RwLock;
10use async_trait::async_trait;
11use hotshot_task_impls::events::{HotShotEvent, HotShotEvent::*};
12use hotshot_types::{
13 data::null_block,
14 traits::{block_contents::BlockHeader, node_implementation::NodeType},
15};
16
17use crate::predicates::{Predicate, PredicateResult};
18
19type EventCallback<TYPES> = Arc<dyn Fn(Arc<HotShotEvent<TYPES>>) -> bool + Send + Sync>;
20
21pub struct EventPredicate<TYPES>
22where
23 TYPES: NodeType + Send + Sync,
24{
25 check: EventCallback<TYPES>,
26 info: String,
27}
28
29impl<TYPES: NodeType> std::fmt::Debug for EventPredicate<TYPES> {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{}", self.info)
32 }
33}
34
35#[allow(clippy::type_complexity)]
36pub struct TestPredicate<INPUT> {
37 pub function: Arc<RwLock<dyn FnMut(&INPUT) -> PredicateResult + Send + Sync>>,
38 pub info: String,
39}
40
41impl<INPUT> std::fmt::Debug for TestPredicate<INPUT> {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}", self.info)
44 }
45}
46
47#[async_trait]
48impl<INPUT> Predicate<INPUT> for TestPredicate<INPUT>
49where
50 INPUT: Send + Sync,
51{
52 async fn evaluate(&self, input: &INPUT) -> PredicateResult {
53 let mut function = self.function.write().await;
54 function(input)
55 }
56
57 async fn info(&self) -> String {
58 self.info.clone()
59 }
60}
61
62pub fn all<TYPES>(events: Vec<HotShotEvent<TYPES>>) -> Box<TestPredicate<Arc<HotShotEvent<TYPES>>>>
63where
64 TYPES: NodeType,
65{
66 all_predicates(events.into_iter().map(exact).collect())
67}
68
69pub fn all_predicates<TYPES: NodeType>(
70 predicates: Vec<Box<EventPredicate<TYPES>>>,
71) -> Box<TestPredicate<Arc<HotShotEvent<TYPES>>>> {
72 let info = format!("{predicates:?}");
73
74 let mut unsatisfied: Vec<_> = predicates.into_iter().map(Arc::new).collect();
75
76 let function = move |e: &Arc<HotShotEvent<TYPES>>| {
77 if !unsatisfied
78 .clone()
79 .into_iter()
80 .any(|pred| (pred.check)(e.clone()))
81 {
82 return PredicateResult::Fail;
83 }
84
85 unsatisfied.retain(|pred| !(pred.check)(e.clone()));
86
87 if unsatisfied.is_empty() {
88 PredicateResult::Pass
89 } else {
90 PredicateResult::Incomplete
91 }
92 };
93
94 Box::new(TestPredicate {
95 function: Arc::new(RwLock::new(function)),
96 info,
97 })
98}
99
100#[macro_export]
101macro_rules! all_predicates {
102 ($($x:expr),* $(,)?) => {
103 {
104 vec![all_predicates(vec![$($x),*])]
105 }
106 };
107}
108
109#[async_trait]
110impl<TYPES> Predicate<Arc<HotShotEvent<TYPES>>> for EventPredicate<TYPES>
111where
112 TYPES: NodeType + Send + Sync + 'static,
113{
114 async fn evaluate(&self, input: &Arc<HotShotEvent<TYPES>>) -> PredicateResult {
115 PredicateResult::from((self.check)(input.clone()))
116 }
117
118 async fn info(&self) -> String {
119 self.info.clone()
120 }
121}
122
123pub fn exact<TYPES>(event: HotShotEvent<TYPES>) -> Box<EventPredicate<TYPES>>
124where
125 TYPES: NodeType,
126{
127 let info = format!("{event:?}");
128 let event = Arc::new(event);
129
130 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
131 let event_clone = event.clone();
132 *e == *event_clone
133 });
134
135 Box::new(EventPredicate { check, info })
136}
137
138pub fn quorum_vote_send<TYPES>() -> Box<EventPredicate<TYPES>>
139where
140 TYPES: NodeType,
141{
142 let info = "QuorumVoteSend".to_string();
143 let check: EventCallback<TYPES> =
144 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), QuorumVoteSend(_)));
145
146 Box::new(EventPredicate { check, info })
147}
148
149pub fn leaves_decided<TYPES>() -> Box<EventPredicate<TYPES>>
150where
151 TYPES: NodeType,
152{
153 let info = "LeavesDecided".to_string();
154 let check: EventCallback<TYPES> =
155 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), LeavesDecided(_)));
156
157 Box::new(EventPredicate { check, info })
158}
159
160pub fn view_change<TYPES>() -> Box<EventPredicate<TYPES>>
161where
162 TYPES: NodeType,
163{
164 let info = "ViewChange".to_string();
165 let check: EventCallback<TYPES> =
166 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), ViewChange(_, _)));
167 Box::new(EventPredicate { check, info })
168}
169
170pub fn upgrade_certificate_formed<TYPES>() -> Box<EventPredicate<TYPES>>
171where
172 TYPES: NodeType,
173{
174 let info = "UpgradeCertificateFormed".to_string();
175 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
176 matches!(e.as_ref(), UpgradeCertificateFormed(_))
177 });
178 Box::new(EventPredicate { check, info })
179}
180
181pub fn quorum_proposal_send_with_upgrade_certificate<TYPES>() -> Box<EventPredicate<TYPES>>
182where
183 TYPES: NodeType,
184{
185 let info = "QuorumProposalSend with UpgradeCertificate attached".to_string();
186 let check: EventCallback<TYPES> =
187 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| match e.as_ref() {
188 QuorumProposalSend(proposal, _) => proposal.data.upgrade_certificate().is_some(),
189 _ => false,
190 });
191 Box::new(EventPredicate { info, check })
192}
193
194pub fn quorum_proposal_validated<TYPES>() -> Box<EventPredicate<TYPES>>
195where
196 TYPES: NodeType,
197{
198 let info = "QuorumProposalValidated".to_string();
199 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
200 matches!(*e.clone(), QuorumProposalValidated(..))
201 });
202 Box::new(EventPredicate { check, info })
203}
204
205pub fn quorum_proposal_send<TYPES>() -> Box<EventPredicate<TYPES>>
206where
207 TYPES: NodeType,
208{
209 let info = "QuorumProposalSend".to_string();
210 let check: EventCallback<TYPES> =
211 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), QuorumProposalSend(..)));
212 Box::new(EventPredicate { check, info })
213}
214
215pub fn quorum_proposal_send_with_null_block<TYPES>(
216 num_storage_nodes: usize,
217) -> Box<EventPredicate<TYPES>>
218where
219 TYPES: NodeType,
220{
221 let info = "QuorumProposalSend with null block payload".to_string();
222 let check: EventCallback<TYPES> =
223 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| match e.as_ref() {
224 QuorumProposalSend(proposal, _) => {
225 Some(proposal.data.block_header().payload_commitment())
226 == null_block::commitment(num_storage_nodes)
227 },
228 _ => false,
229 });
230 Box::new(EventPredicate { check, info })
231}
232
233pub fn timeout_vote_send<TYPES>() -> Box<EventPredicate<TYPES>>
234where
235 TYPES: NodeType,
236{
237 let info = "TimeoutVoteSend".to_string();
238 let check: EventCallback<TYPES> =
239 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), TimeoutVoteSend(..)));
240 Box::new(EventPredicate { check, info })
241}
242
243pub fn view_sync_timeout<TYPES>() -> Box<EventPredicate<TYPES>>
244where
245 TYPES: NodeType,
246{
247 let info = "ViewSyncTimeout".to_string();
248 let check: EventCallback<TYPES> =
249 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), ViewSyncTimeout(..)));
250 Box::new(EventPredicate { check, info })
251}
252
253pub fn view_sync_precommit_vote_send<TYPES>() -> Box<EventPredicate<TYPES>>
254where
255 TYPES: NodeType,
256{
257 let info = "ViewSyncPreCommitVoteSend".to_string();
258 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
259 matches!(e.as_ref(), ViewSyncPreCommitVoteSend(..))
260 });
261 Box::new(EventPredicate { check, info })
262}
263
264pub fn vid_share_validated<TYPES>() -> Box<EventPredicate<TYPES>>
265where
266 TYPES: NodeType,
267{
268 let info = "VidShareValidated".to_string();
269 let check: EventCallback<TYPES> =
270 Arc::new(move |e: Arc<HotShotEvent<TYPES>>| matches!(e.as_ref(), VidShareValidated(..)));
271 Box::new(EventPredicate { check, info })
272}
273
274pub fn da_certificate_validated<TYPES>() -> Box<EventPredicate<TYPES>>
275where
276 TYPES: NodeType,
277{
278 let info = "DaCertificateValidated".to_string();
279 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
280 matches!(e.as_ref(), DaCertificateValidated(..))
281 });
282 Box::new(EventPredicate { check, info })
283}
284
285pub fn quorum_proposal_preliminarily_validated<TYPES>() -> Box<EventPredicate<TYPES>>
286where
287 TYPES: NodeType,
288{
289 let info = "QuorumProposalPreliminarilyValidated".to_string();
290 let check: EventCallback<TYPES> = Arc::new(move |e: Arc<HotShotEvent<TYPES>>| {
291 matches!(e.as_ref(), QuorumProposalPreliminarilyValidated(..))
292 });
293 Box::new(EventPredicate { check, info })
294}