hotshot_testing/predicates/
event.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7use 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}