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::{
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}