hotshot_testing/byzantine/
byzantine_behaviour.rs

1use std::{
2    collections::{BTreeMap, HashMap, HashSet},
3    iter::once,
4    sync::Arc,
5};
6
7use anyhow::Context;
8use async_lock::RwLock;
9use async_trait::async_trait;
10use hotshot::{
11    tasks::EventTransformerState,
12    types::{SignatureKey, SystemContextHandle},
13};
14use hotshot_task_impls::{
15    events::HotShotEvent,
16    network::{
17        test::{ModifierClosure, NetworkEventTaskStateModifier},
18        NetworkEventTaskState,
19    },
20};
21use hotshot_types::{
22    consensus::OuterConsensus,
23    data::QuorumProposalWrapper,
24    epoch_membership::EpochMembershipCoordinator,
25    message::{
26        convert_proposal, GeneralConsensusMessage, Message, MessageKind, Proposal,
27        SequencingMessage, UpgradeLock,
28    },
29    simple_vote::{
30        HasEpoch, QuorumVote2, ViewSyncPreCommitData, ViewSyncPreCommitData2,
31        ViewSyncPreCommitVote, ViewSyncPreCommitVote2,
32    },
33    traits::{
34        election::Membership,
35        network::ConnectedNetwork,
36        node_implementation::{ConsensusTime, NodeImplementation, NodeType, Versions},
37    },
38    vote::HasViewNumber,
39};
40
41#[derive(Debug)]
42/// An `EventTransformerState` that multiplies `QuorumProposalSend` events, incrementing the view number of the proposal
43pub struct BadProposalViewDos {
44    /// The number of times to duplicate a `QuorumProposalSend` event
45    pub multiplier: u64,
46    /// The view number increment each time it's duplicatedjust
47    pub increment: u64,
48}
49
50#[async_trait]
51impl<TYPES: NodeType, I: NodeImplementation<TYPES>, V: Versions> EventTransformerState<TYPES, I, V>
52    for BadProposalViewDos
53{
54    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
55        vec![event.clone()]
56    }
57
58    async fn send_handler(
59        &mut self,
60        event: &HotShotEvent<TYPES>,
61        _public_key: &TYPES::SignatureKey,
62        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
63        _upgrade_lock: &UpgradeLock<TYPES, V>,
64        consensus: OuterConsensus<TYPES>,
65        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
66        _network: Arc<I::Network>,
67    ) -> Vec<HotShotEvent<TYPES>> {
68        match event {
69            HotShotEvent::QuorumProposalSend(proposal, signature) => {
70                let mut result = Vec::new();
71
72                for n in 1..self.multiplier {
73                    let mut modified_proposal = proposal.clone();
74
75                    modified_proposal.data.proposal.view_number += n * self.increment;
76
77                    result.push(HotShotEvent::QuorumProposalSend(
78                        modified_proposal,
79                        signature.clone(),
80                    ));
81                }
82
83                consensus.write().await.reset_actions();
84                result
85            },
86            _ => vec![event.clone()],
87        }
88    }
89}
90
91#[derive(Debug)]
92/// An `EventHandlerState` that doubles the `QuorumVoteSend` and `QuorumProposalSend` events
93pub struct DoubleProposeVote;
94
95#[async_trait]
96impl<TYPES: NodeType, I: NodeImplementation<TYPES>, V: Versions> EventTransformerState<TYPES, I, V>
97    for DoubleProposeVote
98{
99    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
100        vec![event.clone()]
101    }
102
103    async fn send_handler(
104        &mut self,
105        event: &HotShotEvent<TYPES>,
106        _public_key: &TYPES::SignatureKey,
107        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
108        _upgrade_lock: &UpgradeLock<TYPES, V>,
109        _consensus: OuterConsensus<TYPES>,
110        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
111        _network: Arc<I::Network>,
112    ) -> Vec<HotShotEvent<TYPES>> {
113        match event {
114            HotShotEvent::QuorumProposalSend(..) | HotShotEvent::QuorumVoteSend(_) => {
115                vec![event.clone(), event.clone()]
116            },
117            _ => vec![event.clone()],
118        }
119    }
120}
121
122#[derive(Debug)]
123/// An `EventHandlerState` that modifies justify_qc on `QuorumProposalSend` to that of a previous view to mock dishonest leader
124pub struct DishonestLeader<TYPES: NodeType> {
125    /// Store events from previous views
126    pub validated_proposals: Vec<QuorumProposalWrapper<TYPES>>,
127    /// How many times current node has been elected leader and sent proposal
128    pub total_proposals_from_node: u64,
129    /// Which proposals to be dishonest at
130    pub dishonest_at_proposal_numbers: HashSet<u64>,
131    /// How far back to look for a QC
132    pub view_look_back: usize,
133    /// Shared state of all view numbers we send bad proposal at
134    pub dishonest_proposal_view_numbers: Arc<RwLock<HashSet<TYPES::View>>>,
135}
136
137/// Add method that will handle `QuorumProposalSend` events
138/// If we have previous proposals stored and the total_proposals_from_node matches a value specified in dishonest_at_proposal_numbers
139/// Then send out the event with the modified proposal that has an older QC
140impl<TYPES: NodeType> DishonestLeader<TYPES> {
141    /// When a leader is sending a proposal this method will mock a dishonest leader
142    /// We accomplish this by looking back a number of specified views and using that cached proposals QC
143    async fn handle_proposal_send_event(
144        &self,
145        event: &HotShotEvent<TYPES>,
146        proposal: &Proposal<TYPES, QuorumProposalWrapper<TYPES>>,
147        sender: &TYPES::SignatureKey,
148    ) -> HotShotEvent<TYPES> {
149        let length = self.validated_proposals.len();
150        if !self
151            .dishonest_at_proposal_numbers
152            .contains(&self.total_proposals_from_node)
153            || length == 0
154        {
155            return event.clone();
156        }
157
158        // Grab proposal from specified view look back
159        let proposal_from_look_back = if length - 1 < self.view_look_back {
160            // If look back is too far just take the first proposal
161            self.validated_proposals[0].clone()
162        } else {
163            let index = (self.validated_proposals.len() - 1) - self.view_look_back;
164            self.validated_proposals[index].clone()
165        };
166
167        // Create a dishonest proposal by using the old proposals qc
168        let mut dishonest_proposal = proposal.clone();
169        dishonest_proposal.data.proposal.justify_qc = proposal_from_look_back.proposal.justify_qc;
170
171        // Save the view we sent the dishonest proposal on (used for coordination attacks with other byzantine replicas)
172        let mut dishonest_proposal_sent = self.dishonest_proposal_view_numbers.write().await;
173        dishonest_proposal_sent.insert(proposal.data.view_number());
174
175        HotShotEvent::QuorumProposalSend(dishonest_proposal, sender.clone())
176    }
177}
178
179#[async_trait]
180impl<TYPES: NodeType, I: NodeImplementation<TYPES> + std::fmt::Debug, V: Versions>
181    EventTransformerState<TYPES, I, V> for DishonestLeader<TYPES>
182{
183    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
184        vec![event.clone()]
185    }
186
187    async fn send_handler(
188        &mut self,
189        event: &HotShotEvent<TYPES>,
190        _public_key: &TYPES::SignatureKey,
191        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
192        _upgrade_lock: &UpgradeLock<TYPES, V>,
193        _consensus: OuterConsensus<TYPES>,
194        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
195        _network: Arc<I::Network>,
196    ) -> Vec<HotShotEvent<TYPES>> {
197        match event {
198            HotShotEvent::QuorumProposalSend(proposal, sender) => {
199                self.total_proposals_from_node += 1;
200                return vec![
201                    self.handle_proposal_send_event(event, proposal, sender)
202                        .await,
203                ];
204            },
205            HotShotEvent::QuorumProposalValidated(proposal, _) => {
206                self.validated_proposals.push(proposal.data.clone());
207            },
208            _ => {},
209        }
210        vec![event.clone()]
211    }
212}
213
214#[derive(Debug)]
215/// An `EventHandlerState` that modifies view number on the certificate of `DacSend` event to that of a future view
216pub struct DishonestDa {
217    /// How many times current node has been elected leader and sent Da Cert
218    pub total_da_certs_sent_from_node: u64,
219    /// Which proposals to be dishonest at
220    pub dishonest_at_da_cert_sent_numbers: HashSet<u64>,
221    /// When leader how many times we will send DacSend and increment view number
222    pub total_views_add_to_cert: u64,
223}
224
225#[async_trait]
226impl<TYPES: NodeType, I: NodeImplementation<TYPES> + std::fmt::Debug, V: Versions>
227    EventTransformerState<TYPES, I, V> for DishonestDa
228{
229    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
230        vec![event.clone()]
231    }
232
233    async fn send_handler(
234        &mut self,
235        event: &HotShotEvent<TYPES>,
236        _public_key: &TYPES::SignatureKey,
237        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
238        _upgrade_lock: &UpgradeLock<TYPES, V>,
239        _consensus: OuterConsensus<TYPES>,
240        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
241        _network: Arc<I::Network>,
242    ) -> Vec<HotShotEvent<TYPES>> {
243        if let HotShotEvent::DacSend(cert, sender) = event {
244            self.total_da_certs_sent_from_node += 1;
245            if self
246                .dishonest_at_da_cert_sent_numbers
247                .contains(&self.total_da_certs_sent_from_node)
248            {
249                let mut result = vec![HotShotEvent::DacSend(cert.clone(), sender.clone())];
250                for i in 1..=self.total_views_add_to_cert {
251                    let mut bad_cert = cert.clone();
252                    bad_cert.view_number = cert.view_number + i;
253                    result.push(HotShotEvent::DacSend(bad_cert, sender.clone()));
254                }
255                return result;
256            }
257        }
258        vec![event.clone()]
259    }
260}
261
262/// View delay configuration
263#[derive(Debug)]
264pub struct ViewDelay<TYPES: NodeType> {
265    /// How many views the node will be delayed
266    pub number_of_views_to_delay: u64,
267    /// A map that is from view number to vector of events
268    pub events_for_view: HashMap<TYPES::View, Vec<HotShotEvent<TYPES>>>,
269    /// Specify which view number to stop delaying
270    pub stop_view_delay_at_view_number: u64,
271}
272
273#[async_trait]
274impl<TYPES: NodeType, I: NodeImplementation<TYPES> + std::fmt::Debug, V: Versions>
275    EventTransformerState<TYPES, I, V> for ViewDelay<TYPES>
276{
277    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
278        let correct_event = vec![event.clone()];
279        if let Some(view_number) = event.view_number() {
280            if *view_number >= self.stop_view_delay_at_view_number {
281                return correct_event;
282            }
283
284            // add current view or push event to the map if view number has been added
285            let events_for_current_view = self.events_for_view.entry(view_number).or_default();
286            events_for_current_view.push(event.clone());
287
288            // ensure we are actually able to lookback enough views
289            let view_diff = (*view_number).saturating_sub(self.number_of_views_to_delay);
290            if view_diff > 0 {
291                return match self
292                    .events_for_view
293                    .remove(&<TYPES as NodeType>::View::new(view_diff))
294                {
295                    Some(lookback_events) => lookback_events.clone(),
296                    // we have already return all received events for this view
297                    None => vec![],
298                };
299            }
300        }
301
302        correct_event
303    }
304
305    async fn send_handler(
306        &mut self,
307        event: &HotShotEvent<TYPES>,
308        _public_key: &TYPES::SignatureKey,
309        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
310        _upgrade_lock: &UpgradeLock<TYPES, V>,
311        _consensus: OuterConsensus<TYPES>,
312        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
313        _network: Arc<I::Network>,
314    ) -> Vec<HotShotEvent<TYPES>> {
315        vec![event.clone()]
316    }
317}
318
319/// An `EventHandlerState` that modifies view number on the vote of `QuorumVoteSend` event to that of a future view and correctly signs the vote
320pub struct DishonestVoting<TYPES: NodeType> {
321    /// Number added to the original vote's view number
322    pub view_increment: u64,
323    /// A function passed to `NetworkEventTaskStateModifier` to modify `NetworkEventTaskState` behaviour.
324    pub modifier: Arc<ModifierClosure<TYPES>>,
325}
326
327#[async_trait]
328impl<TYPES: NodeType, I: NodeImplementation<TYPES> + std::fmt::Debug, V: Versions>
329    EventTransformerState<TYPES, I, V> for DishonestVoting<TYPES>
330{
331    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
332        vec![event.clone()]
333    }
334
335    async fn send_handler(
336        &mut self,
337        event: &HotShotEvent<TYPES>,
338        public_key: &TYPES::SignatureKey,
339        private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
340        upgrade_lock: &UpgradeLock<TYPES, V>,
341        _consensus: OuterConsensus<TYPES>,
342        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
343        _network: Arc<I::Network>,
344    ) -> Vec<HotShotEvent<TYPES>> {
345        if let HotShotEvent::QuorumVoteSend(vote) = event {
346            let new_view = vote.view_number + self.view_increment;
347            let spoofed_vote = QuorumVote2::<TYPES>::create_signed_vote(
348                vote.data.clone(),
349                new_view,
350                public_key,
351                private_key,
352                upgrade_lock,
353            )
354            .await
355            .context("Failed to sign vote")
356            .unwrap();
357            tracing::debug!("Sending Quorum Vote for view: {new_view:?}");
358            return vec![HotShotEvent::QuorumVoteSend(spoofed_vote)];
359        }
360        vec![event.clone()]
361    }
362
363    fn add_network_event_task(
364        &self,
365        handle: &mut SystemContextHandle<TYPES, I, V>,
366        network: Arc<<I as NodeImplementation<TYPES>>::Network>,
367    ) {
368        let network_state: NetworkEventTaskState<_, V, _, _> = NetworkEventTaskState {
369            network,
370            view: TYPES::View::genesis(),
371            epoch: None,
372            membership_coordinator: handle.membership_coordinator.clone(),
373            storage: handle.storage(),
374            storage_metrics: handle.storage_metrics(),
375            consensus: OuterConsensus::new(handle.consensus()),
376            upgrade_lock: handle.hotshot.upgrade_lock.clone(),
377            transmit_tasks: BTreeMap::new(),
378            epoch_height: handle.epoch_height,
379            id: handle.hotshot.id,
380        };
381        let modified_network_state = NetworkEventTaskStateModifier {
382            network_event_task_state: network_state,
383            modifier: Arc::clone(&self.modifier),
384        };
385        handle.add_task(modified_network_state);
386    }
387}
388
389impl<TYPES: NodeType> std::fmt::Debug for DishonestVoting<TYPES> {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        f.debug_struct("DishonestVoting")
392            .field("view_increment", &self.view_increment)
393            .finish_non_exhaustive()
394    }
395}
396
397#[derive(Debug)]
398/// An `EventHandlerState` that will send a vote for a bad proposal
399pub struct DishonestVoter<TYPES: NodeType> {
400    /// Collect all votes the node sends
401    pub votes_sent: Vec<QuorumVote2<TYPES>>,
402    /// Shared state with views numbers that leaders were dishonest at
403    pub dishonest_proposal_view_numbers: Arc<RwLock<HashSet<TYPES::View>>>,
404}
405
406#[async_trait]
407impl<TYPES: NodeType, I: NodeImplementation<TYPES> + std::fmt::Debug, V: Versions>
408    EventTransformerState<TYPES, I, V> for DishonestVoter<TYPES>
409{
410    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
411        vec![event.clone()]
412    }
413
414    async fn send_handler(
415        &mut self,
416        event: &HotShotEvent<TYPES>,
417        public_key: &TYPES::SignatureKey,
418        private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
419        upgrade_lock: &UpgradeLock<TYPES, V>,
420        _consensus: OuterConsensus<TYPES>,
421        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
422        _network: Arc<I::Network>,
423    ) -> Vec<HotShotEvent<TYPES>> {
424        match event {
425            HotShotEvent::QuorumProposalRecv(proposal, _sender) => {
426                // Check if view is a dishonest proposal, if true send a vote
427                let dishonest_proposals = self.dishonest_proposal_view_numbers.read().await;
428                if dishonest_proposals.contains(&proposal.data.view_number()) {
429                    // Create a vote using data from most recent vote and the current event number
430                    // We wont update internal consensus state for this Byzantine replica but we are at least
431                    // Going to send a vote to the next honest leader
432                    let vote = QuorumVote2::<TYPES>::create_signed_vote(
433                        self.votes_sent.last().unwrap().data.clone(),
434                        event.view_number().unwrap(),
435                        public_key,
436                        private_key,
437                        upgrade_lock,
438                    )
439                    .await
440                    .context("Failed to sign vote")
441                    .unwrap();
442                    return vec![HotShotEvent::QuorumVoteSend(vote)];
443                }
444            },
445            HotShotEvent::TimeoutVoteSend(vote) => {
446                // Check if this view was a dishonest proposal view, if true dont send timeout
447                let dishonest_proposals = self.dishonest_proposal_view_numbers.read().await;
448                if dishonest_proposals.contains(&vote.view_number) {
449                    // We craft the vote upon `QuorumProposalRecv` and send out a vote.
450                    // So, dont send the timeout to the next leader from this byzantine replica
451                    return vec![];
452                }
453            },
454            HotShotEvent::QuorumVoteSend(vote) => {
455                self.votes_sent.push(vote.clone());
456            },
457            _ => {},
458        }
459        vec![event.clone()]
460    }
461}
462
463/// Implements a byzantine behaviour which aims at splitting the honest nodes during view sync protocol
464/// so that the honest nodes cannot view sync on their own.
465///
466/// Requirement: The scenario requires at least 4 dishonest nodes so total number of nodes need to be
467/// at least 13.
468///
469/// Scenario:
470/// 1. The first dishonest leader sends a proposal to only f + 1 honest nodes and f dishonest nodes
471/// 2. The second dishonest leader sends a proposal to only f + 1 honest nodes.
472/// 3. All dishonest nodes do not send timeout votes.
473/// 4. The first dishonest relay sends a correctly formed precommit certificate to f + 1 honest nodes
474///    and f dishonest nodes.
475/// 5. The first dishonest relay sends a correctly formed commit certificate to only one honest node.
476/// 6. The second dishonest relay behaves in the same way as the first dishonest relay.
477#[derive(Debug)]
478pub struct DishonestViewSyncRelay {
479    pub dishonest_proposal_view_numbers: Vec<u64>,
480    pub dishonest_vote_view_numbers: Vec<u64>,
481    pub first_f_honest_nodes: Vec<u64>,
482    pub second_f_honest_nodes: Vec<u64>,
483    pub one_honest_node: u64,
484    pub f_dishonest_nodes: Vec<u64>,
485}
486
487#[async_trait]
488impl<TYPES: NodeType, I: NodeImplementation<TYPES>, V: Versions> EventTransformerState<TYPES, I, V>
489    for DishonestViewSyncRelay
490{
491    async fn send_handler(
492        &mut self,
493        event: &HotShotEvent<TYPES>,
494        _public_key: &TYPES::SignatureKey,
495        _private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
496        upgrade_lock: &UpgradeLock<TYPES, V>,
497        _consensus: OuterConsensus<TYPES>,
498        membership_coordinator: EpochMembershipCoordinator<TYPES>,
499        network: Arc<I::Network>,
500    ) -> Vec<HotShotEvent<TYPES>> {
501        match event {
502            HotShotEvent::QuorumProposalSend(proposal, sender) => {
503                let view_number = proposal.data.view_number();
504                if !self.dishonest_proposal_view_numbers.contains(&view_number) {
505                    return vec![event.clone()];
506                }
507                let message_kind = if upgrade_lock.epochs_enabled(view_number).await {
508                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
509                        GeneralConsensusMessage::Proposal2(convert_proposal(proposal.clone())),
510                    ))
511                } else {
512                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
513                        GeneralConsensusMessage::Proposal(convert_proposal(proposal.clone())),
514                    ))
515                };
516                let message = Message {
517                    sender: sender.clone(),
518                    kind: message_kind,
519                };
520                let serialized_message = match upgrade_lock.serialize(&message).await {
521                    Ok(serialized) => serialized,
522                    Err(e) => {
523                        panic!("Failed to serialize message: {e}");
524                    },
525                };
526                let second_f_honest_it = self.second_f_honest_nodes.iter();
527                let f_dishonest_it = self.f_dishonest_nodes.iter();
528                let one_honest_it = once(&self.one_honest_node);
529                let chained_it: Box<dyn Iterator<Item = &u64> + Send> =
530                    if &*view_number == self.dishonest_proposal_view_numbers.first().unwrap() {
531                        // The first dishonest proposal is sent to f + 1 honest nodes and f dishonest nodes
532                        Box::new(second_f_honest_it.chain(one_honest_it.chain(f_dishonest_it)))
533                    } else {
534                        // All other dishonest proposals are sent to f + 1 honest nodes
535                        Box::new(second_f_honest_it.chain(one_honest_it))
536                    };
537                for node_id in chained_it {
538                    let dummy_view = TYPES::View::new(*node_id);
539                    let Ok(node) = membership_coordinator
540                        .membership()
541                        .read()
542                        .await
543                        .leader(dummy_view, proposal.data.epoch())
544                    else {
545                        panic!(
546                            "Failed to find leader for view {} and epoch {:?}",
547                            dummy_view,
548                            proposal.data.epoch()
549                        );
550                    };
551                    let transmit_result = network
552                        .direct_message(serialized_message.clone(), node.clone())
553                        .await;
554                    match transmit_result {
555                        Ok(()) => tracing::info!(
556                            "Sent proposal for view {} to node {}",
557                            proposal.data.view_number(),
558                            node_id
559                        ),
560                        Err(e) => panic!("Failed to send message task: {e:?}"),
561                    }
562                }
563                vec![]
564            },
565            HotShotEvent::QuorumVoteSend(vote) => {
566                if !self.dishonest_vote_view_numbers.contains(&vote.view_number) {
567                    return vec![event.clone()];
568                }
569                vec![]
570            },
571            HotShotEvent::TimeoutVoteSend(vote) => {
572                if !self.dishonest_vote_view_numbers.contains(&vote.view_number) {
573                    return vec![event.clone()];
574                }
575                vec![]
576            },
577            HotShotEvent::ViewSyncPreCommitVoteSend(vote) => {
578                if !self.dishonest_vote_view_numbers.contains(&vote.view_number) {
579                    return vec![event.clone()];
580                }
581                vec![]
582            },
583            HotShotEvent::ViewSyncPreCommitCertificateSend(certificate, sender) => {
584                let view_number = certificate.data.round;
585                if !self.dishonest_proposal_view_numbers.contains(&view_number) {
586                    return vec![event.clone()];
587                }
588                let message_kind = if upgrade_lock.epochs_enabled(view_number).await {
589                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
590                        GeneralConsensusMessage::ViewSyncPreCommitCertificate2(certificate.clone()),
591                    ))
592                } else {
593                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
594                        GeneralConsensusMessage::ViewSyncPreCommitCertificate(
595                            certificate.clone().to_vsc(),
596                        ),
597                    ))
598                };
599                let message = Message {
600                    sender: sender.clone(),
601                    kind: message_kind,
602                };
603                let serialized_message = match upgrade_lock.serialize(&message).await {
604                    Ok(serialized) => serialized,
605                    Err(e) => {
606                        panic!("Failed to serialize message: {e}");
607                    },
608                };
609                let second_f_honest_it = self.second_f_honest_nodes.iter();
610                let f_dishonest_it = self.f_dishonest_nodes.iter();
611                let one_honest_it = once(&self.one_honest_node);
612                // The pre-commit certificate is sent to f + 1 honest nodes and f dishonest nodes
613                let chained_it: Box<dyn Iterator<Item = &u64> + Send> =
614                    Box::new(second_f_honest_it.chain(one_honest_it.chain(f_dishonest_it)));
615                for node_id in chained_it {
616                    let dummy_view = TYPES::View::new(*node_id);
617                    let Ok(node) = membership_coordinator
618                        .membership()
619                        .read()
620                        .await
621                        .leader(dummy_view, certificate.epoch())
622                    else {
623                        panic!(
624                            "Failed to find leader for view {} and epoch {:?}",
625                            dummy_view,
626                            certificate.epoch()
627                        );
628                    };
629                    let transmit_result = network
630                        .direct_message(serialized_message.clone(), node.clone())
631                        .await;
632                    match transmit_result {
633                        Ok(()) => tracing::info!(
634                            "Sent ViewSyncPreCommitCertificate for view {} to node {}",
635                            view_number,
636                            node_id
637                        ),
638                        Err(e) => panic!("Failed to send message task: {e:?}"),
639                    }
640                }
641                vec![]
642            },
643            HotShotEvent::ViewSyncCommitCertificateSend(certificate, sender) => {
644                let view_number = certificate.data.round;
645                if !self.dishonest_proposal_view_numbers.contains(&view_number) {
646                    return vec![event.clone()];
647                }
648                let message_kind = if upgrade_lock.epochs_enabled(view_number).await {
649                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
650                        GeneralConsensusMessage::ViewSyncCommitCertificate2(certificate.clone()),
651                    ))
652                } else {
653                    MessageKind::<TYPES>::from_consensus_message(SequencingMessage::General(
654                        GeneralConsensusMessage::ViewSyncCommitCertificate(
655                            certificate.clone().to_vsc(),
656                        ),
657                    ))
658                };
659                let message = Message {
660                    sender: sender.clone(),
661                    kind: message_kind,
662                };
663                let serialized_message = match upgrade_lock.serialize(&message).await {
664                    Ok(serialized) => serialized,
665                    Err(e) => {
666                        panic!("Failed to serialize message: {e}");
667                    },
668                };
669                let one_honest_it = once(&self.one_honest_node);
670                // The commit certificate is sent to 1 honest node
671                let chained_it: Box<dyn Iterator<Item = &u64> + Send> = Box::new(one_honest_it);
672                for node_id in chained_it {
673                    let dummy_view = TYPES::View::new(*node_id);
674                    let Ok(node) = membership_coordinator
675                        .membership()
676                        .read()
677                        .await
678                        .leader(dummy_view, certificate.epoch())
679                    else {
680                        panic!(
681                            "Failed to find leader for view {} and epoch {:?}",
682                            dummy_view,
683                            certificate.epoch()
684                        );
685                    };
686                    let transmit_result = network
687                        .direct_message(serialized_message.clone(), node.clone())
688                        .await;
689                    match transmit_result {
690                        Ok(()) => tracing::info!(
691                            "Sent ViewSyncCommitCertificate for view {} to node {}",
692                            view_number,
693                            node_id
694                        ),
695                        Err(e) => panic!("Failed to send message task: {e:?}"),
696                    }
697                }
698                vec![]
699            },
700            _ => vec![event.clone()],
701        }
702    }
703
704    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
705        vec![event.clone()]
706    }
707}
708
709#[derive(Debug)]
710pub struct DishonestViewSyncWrongEpoch<TYPES: NodeType> {
711    pub first_dishonest_view_number: u64,
712    pub epoch_modifier: fn(TYPES::Epoch) -> TYPES::Epoch,
713}
714
715#[async_trait]
716impl<TYPES: NodeType, I: NodeImplementation<TYPES>, V: Versions> EventTransformerState<TYPES, I, V>
717    for DishonestViewSyncWrongEpoch<TYPES>
718{
719    async fn send_handler(
720        &mut self,
721        event: &HotShotEvent<TYPES>,
722        public_key: &TYPES::SignatureKey,
723        private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
724        upgrade_lock: &UpgradeLock<TYPES, V>,
725        _consensus: OuterConsensus<TYPES>,
726        _membership_coordinator: EpochMembershipCoordinator<TYPES>,
727        _network: Arc<I::Network>,
728    ) -> Vec<HotShotEvent<TYPES>> {
729        match event {
730            HotShotEvent::QuorumProposalSend(proposal, _) => {
731                if self.first_dishonest_view_number > proposal.data.view_number().u64() {
732                    return vec![event.clone()];
733                }
734                vec![]
735            },
736            HotShotEvent::QuorumVoteSend(vote) => {
737                if self.first_dishonest_view_number > vote.view_number().u64() {
738                    return vec![event.clone()];
739                }
740                vec![]
741            },
742            HotShotEvent::TimeoutVoteSend(vote) => {
743                if self.first_dishonest_view_number > vote.view_number().u64() {
744                    return vec![event.clone()];
745                }
746                vec![]
747            },
748            HotShotEvent::ViewSyncPreCommitVoteSend(vote) => {
749                if self.first_dishonest_view_number > vote.view_number().u64() {
750                    return vec![event.clone()];
751                }
752                let view_number = vote.data.round;
753                let vote = if upgrade_lock.epochs_enabled(view_number).await {
754                    ViewSyncPreCommitVote2::<TYPES>::create_signed_vote(
755                        ViewSyncPreCommitData2 {
756                            relay: 0,
757                            round: view_number,
758                            epoch: vote.data.epoch.map(self.epoch_modifier),
759                        },
760                        view_number,
761                        public_key,
762                        private_key,
763                        upgrade_lock,
764                    )
765                    .await
766                    .context("Failed to sign pre commit vote!")
767                    .unwrap()
768                } else {
769                    let vote = ViewSyncPreCommitVote::<TYPES>::create_signed_vote(
770                        ViewSyncPreCommitData {
771                            relay: 0,
772                            round: view_number,
773                        },
774                        view_number,
775                        public_key,
776                        private_key,
777                        upgrade_lock,
778                    )
779                    .await
780                    .context("Failed to sign pre commit vote!")
781                    .unwrap();
782                    vote.to_vote2()
783                };
784                vec![HotShotEvent::ViewSyncPreCommitVoteSend(vote)]
785            },
786            _ => vec![event.clone()],
787        }
788    }
789
790    async fn recv_handler(&mut self, event: &HotShotEvent<TYPES>) -> Vec<HotShotEvent<TYPES>> {
791        vec![event.clone()]
792    }
793}