Skip to main content

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