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