1use std::{collections::BTreeSet, sync::Arc, time::Duration};
20
21use sc_network::{NetworkPeers, ReputationChange};
22use sc_network_gossip::{MessageIntent, ValidationResult, Validator, ValidatorContext};
23use sc_network_types::PeerId;
24use sp_runtime::traits::{Block, Hash, Header, NumberFor};
25
26use codec::{Decode, DecodeAll, Encode};
27use log::{debug, trace};
28use parking_lot::{Mutex, RwLock};
29use wasm_timer::Instant;
30
31use crate::{
32 communication::{benefit, cost, peers::KnownPeers},
33 justification::{
34 proof_block_num_and_set_id, verify_with_validator_set, BeefyVersionedFinalityProof,
35 },
36 keystore::BeefyKeystore,
37 LOG_TARGET,
38};
39use sp_application_crypto::RuntimeAppPublic;
40use sp_consensus_beefy::{AuthorityIdBound, ValidatorSet, ValidatorSetId, VoteMessage};
41
42#[cfg(not(test))]
44const REBROADCAST_AFTER: Duration = Duration::from_secs(60);
45#[cfg(test)]
46const REBROADCAST_AFTER: Duration = Duration::from_secs(5);
47
48#[derive(Debug, PartialEq)]
49pub(super) enum Action<H> {
50 Keep(H, ReputationChange),
52 Discard(ReputationChange),
54 DiscardNoReport,
56}
57
58#[derive(Debug, PartialEq, Clone, Copy)]
60enum Consider {
61 Accept,
63 RejectPast,
65 RejectFuture,
67 CannotEvaluate,
69}
70
71#[derive(Debug, Encode, Decode)]
73pub(crate) enum GossipMessage<B: Block, AuthorityId: AuthorityIdBound> {
74 Vote(VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>),
76 FinalityProof(BeefyVersionedFinalityProof<B, AuthorityId>),
78}
79
80impl<B: Block, AuthorityId: AuthorityIdBound> GossipMessage<B, AuthorityId> {
81 pub fn unwrap_vote(
83 self,
84 ) -> Option<VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>>
85 {
86 match self {
87 GossipMessage::Vote(vote) => Some(vote),
88 GossipMessage::FinalityProof(_) => None,
89 }
90 }
91
92 pub fn unwrap_finality_proof(self) -> Option<BeefyVersionedFinalityProof<B, AuthorityId>> {
94 match self {
95 GossipMessage::Vote(_) => None,
96 GossipMessage::FinalityProof(proof) => Some(proof),
97 }
98 }
99}
100
101pub(crate) fn votes_topic<B: Block>() -> B::Hash
103where
104 B: Block,
105{
106 <<B::Header as Header>::Hashing as Hash>::hash(b"beefy-votes")
107}
108
109pub(crate) fn proofs_topic<B: Block>() -> B::Hash
111where
112 B: Block,
113{
114 <<B::Header as Header>::Hashing as Hash>::hash(b"beefy-justifications")
115}
116
117#[derive(Clone, Debug)]
118pub(crate) struct GossipFilterCfg<'a, B: Block, AuthorityId: AuthorityIdBound> {
119 pub start: NumberFor<B>,
120 pub end: NumberFor<B>,
121 pub validator_set: &'a ValidatorSet<AuthorityId>,
122}
123
124#[derive(Clone, Debug)]
125struct FilterInner<B: Block, AuthorityId: AuthorityIdBound> {
126 pub start: NumberFor<B>,
127 pub end: NumberFor<B>,
128 pub validator_set: ValidatorSet<AuthorityId>,
129}
130
131struct Filter<B: Block, AuthorityId: AuthorityIdBound> {
132 inner: Option<FilterInner<B, AuthorityId>>,
134 rounds_with_valid_proofs: BTreeSet<NumberFor<B>>,
136}
137
138impl<B: Block, AuthorityId: AuthorityIdBound> Filter<B, AuthorityId> {
139 pub fn new() -> Self {
140 Self { inner: None, rounds_with_valid_proofs: BTreeSet::new() }
141 }
142
143 fn update(&mut self, cfg: GossipFilterCfg<B, AuthorityId>) {
145 self.rounds_with_valid_proofs
146 .retain(|&round| round >= cfg.start && round <= cfg.end);
147 match self.inner.as_mut() {
149 Some(f) if f.validator_set.id() == cfg.validator_set.id() => {
150 f.start = cfg.start;
151 f.end = cfg.end;
152 },
153 _ =>
154 self.inner = Some(FilterInner {
155 start: cfg.start,
156 end: cfg.end,
157 validator_set: cfg.validator_set.clone(),
158 }),
159 }
160 }
161
162 fn consider_vote(&self, round: NumberFor<B>, set_id: ValidatorSetId) -> Consider {
167 self.inner
168 .as_ref()
169 .map(|f|
170 if set_id < f.validator_set.id() || round < f.start {
172 Consider::RejectPast
173 } else if set_id > f.validator_set.id() || round > f.end {
174 Consider::RejectFuture
175 } else {
176 Consider::Accept
177 })
178 .unwrap_or(Consider::CannotEvaluate)
179 }
180
181 fn consider_finality_proof(&self, round: NumberFor<B>, set_id: ValidatorSetId) -> Consider {
186 self.inner
187 .as_ref()
188 .map(|f|
189 if round < f.start || set_id < f.validator_set.id() {
191 Consider::RejectPast
192 } else if set_id > f.validator_set.id() {
193 Consider::RejectFuture
194 } else {
195 Consider::Accept
196 }
197 )
198 .unwrap_or(Consider::CannotEvaluate)
199 }
200
201 fn mark_round_as_proven(&mut self, round: NumberFor<B>) {
203 self.rounds_with_valid_proofs.insert(round);
204 }
205
206 fn is_already_proven(&self, round: NumberFor<B>) -> bool {
208 self.rounds_with_valid_proofs.contains(&round)
209 }
210
211 fn validator_set(&self) -> Option<&ValidatorSet<AuthorityId>> {
212 self.inner.as_ref().map(|f| &f.validator_set)
213 }
214}
215
216pub(crate) struct GossipValidator<B, N, AuthorityId: AuthorityIdBound>
225where
226 B: Block,
227{
228 votes_topic: B::Hash,
229 justifs_topic: B::Hash,
230 gossip_filter: RwLock<Filter<B, AuthorityId>>,
231 next_rebroadcast: Mutex<Instant>,
232 known_peers: Arc<Mutex<KnownPeers<B>>>,
233 network: Arc<N>,
234}
235
236impl<B, N, AuthorityId> GossipValidator<B, N, AuthorityId>
237where
238 B: Block,
239 AuthorityId: AuthorityIdBound,
240{
241 pub(crate) fn new(known_peers: Arc<Mutex<KnownPeers<B>>>, network: Arc<N>) -> Self {
242 Self {
243 votes_topic: votes_topic::<B>(),
244 justifs_topic: proofs_topic::<B>(),
245 gossip_filter: RwLock::new(Filter::new()),
246 next_rebroadcast: Mutex::new(Instant::now() + REBROADCAST_AFTER),
247 known_peers,
248 network,
249 }
250 }
251
252 pub(crate) fn update_filter(&self, filter: GossipFilterCfg<B, AuthorityId>) {
256 debug!(
257 target: LOG_TARGET,
258 "🥩 New gossip filter: start {:?}, end {:?}, validator set id {:?}",
259 filter.start, filter.end, filter.validator_set.id()
260 );
261 self.gossip_filter.write().update(filter);
262 }
263}
264
265impl<B, N, AuthorityId> GossipValidator<B, N, AuthorityId>
266where
267 B: Block,
268 N: NetworkPeers,
269 AuthorityId: AuthorityIdBound,
270{
271 fn report(&self, who: PeerId, cost_benefit: ReputationChange) {
272 self.network.report_peer(who, cost_benefit);
273 }
274
275 fn validate_vote(
276 &self,
277 vote: VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>,
278 sender: &PeerId,
279 ) -> Action<B::Hash> {
280 let round = vote.commitment.block_number;
281 let set_id = vote.commitment.validator_set_id;
282 self.known_peers.lock().note_vote_for(*sender, round);
283
284 {
287 let filter = self.gossip_filter.read();
288
289 match filter.consider_vote(round, set_id) {
290 Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE),
291 Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE),
292 Consider::CannotEvaluate => return Action::DiscardNoReport,
295 Consider::Accept => {},
296 }
297
298 if !filter
300 .validator_set()
301 .map(|set| set.validators().contains(&vote.id))
302 .unwrap_or(false)
303 {
304 debug!(target: LOG_TARGET, "Message from voter not in validator set: {}", vote.id);
305 return Action::Discard(cost::UNKNOWN_VOTER);
306 }
307 }
308
309 if BeefyKeystore::verify(&vote.id, &vote.signature, &vote.commitment.encode()) {
310 Action::Keep(self.votes_topic, benefit::VOTE_MESSAGE)
311 } else {
312 debug!(
313 target: LOG_TARGET,
314 "🥩 Bad signature on message: {:?}, from: {:?}", vote, sender
315 );
316 Action::Discard(cost::BAD_SIGNATURE)
317 }
318 }
319
320 fn validate_finality_proof(
321 &self,
322 proof: BeefyVersionedFinalityProof<B, AuthorityId>,
323 sender: &PeerId,
324 ) -> Action<B::Hash> {
325 let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
326 self.known_peers.lock().note_vote_for(*sender, round);
327
328 let action = {
329 let guard = self.gossip_filter.read();
330
331 match guard.consider_finality_proof(round, set_id) {
333 Consider::RejectPast => return Action::Discard(cost::OUTDATED_MESSAGE),
334 Consider::RejectFuture => return Action::Discard(cost::FUTURE_MESSAGE),
335 Consider::CannotEvaluate => return Action::DiscardNoReport,
338 Consider::Accept => {},
339 }
340
341 if guard.is_already_proven(round) {
342 return Action::Discard(benefit::NOT_INTERESTED);
343 }
344
345 guard
347 .validator_set()
348 .map(|validator_set| {
349 if let Err((_, signatures_checked)) =
350 verify_with_validator_set::<B, AuthorityId>(round, validator_set, &proof)
351 {
352 debug!(
353 target: LOG_TARGET,
354 "🥩 Bad signatures on message: {:?}, from: {:?}", proof, sender
355 );
356 let mut cost = cost::INVALID_PROOF;
357 cost.value +=
358 cost::PER_SIGNATURE_CHECKED.saturating_mul(signatures_checked as i32);
359 Action::Discard(cost)
360 } else {
361 Action::Keep(self.justifs_topic, benefit::VALIDATED_PROOF)
362 }
363 })
364 .unwrap_or(Action::DiscardNoReport)
367 };
368 if matches!(action, Action::Keep(_, _)) {
369 self.gossip_filter.write().mark_round_as_proven(round);
370 }
371 action
372 }
373}
374
375impl<B, N, AuthorityId> Validator<B> for GossipValidator<B, N, AuthorityId>
376where
377 B: Block,
378 AuthorityId: AuthorityIdBound,
379 N: NetworkPeers + Send + Sync,
380{
381 fn peer_disconnected(&self, _context: &mut dyn ValidatorContext<B>, who: &PeerId) {
382 self.known_peers.lock().remove(who);
383 }
384
385 fn validate(
386 &self,
387 context: &mut dyn ValidatorContext<B>,
388 sender: &PeerId,
389 mut data: &[u8],
390 ) -> ValidationResult<B::Hash> {
391 let raw = data;
392 let action = match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
393 Ok(GossipMessage::Vote(msg)) => self.validate_vote(msg, sender),
394 Ok(GossipMessage::FinalityProof(proof)) => self.validate_finality_proof(proof, sender),
395 Err(e) => {
396 debug!(target: LOG_TARGET, "Error decoding message: {}", e);
397 let bytes = raw.len().min(i32::MAX as usize) as i32;
398 let cost = ReputationChange::new(
399 bytes.saturating_mul(cost::PER_UNDECODABLE_BYTE),
400 "BEEFY: Bad packet",
401 );
402 Action::Discard(cost)
403 },
404 };
405 match action {
406 Action::Keep(topic, cb) => {
407 self.report(*sender, cb);
408 context.broadcast_message(topic, data.to_vec(), false);
409 ValidationResult::ProcessAndKeep(topic)
410 },
411 Action::Discard(cb) => {
412 self.report(*sender, cb);
413 ValidationResult::Discard
414 },
415 Action::DiscardNoReport => ValidationResult::Discard,
416 }
417 }
418
419 fn message_expired<'a>(&'a self) -> Box<dyn FnMut(B::Hash, &[u8]) -> bool + 'a> {
420 let filter = self.gossip_filter.read();
421 Box::new(move |_topic, mut data| {
422 match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
423 Ok(GossipMessage::Vote(msg)) => {
424 let round = msg.commitment.block_number;
425 let set_id = msg.commitment.validator_set_id;
426 let expired = filter.consider_vote(round, set_id) != Consider::Accept;
427 trace!(target: LOG_TARGET, "🥩 Vote for round #{} expired: {}", round, expired);
428 expired
429 },
430 Ok(GossipMessage::FinalityProof(proof)) => {
431 let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
432 let expired = filter.consider_finality_proof(round, set_id) != Consider::Accept;
433 trace!(
434 target: LOG_TARGET,
435 "🥩 Finality proof for round #{} expired: {}",
436 round,
437 expired
438 );
439 expired
440 },
441 Err(_) => true,
442 }
443 })
444 }
445
446 fn message_allowed<'a>(
447 &'a self,
448 ) -> Box<dyn FnMut(&PeerId, MessageIntent, &B::Hash, &[u8]) -> bool + 'a> {
449 let do_rebroadcast = {
450 let now = Instant::now();
451 let mut next_rebroadcast = self.next_rebroadcast.lock();
452 if now >= *next_rebroadcast {
453 trace!(target: LOG_TARGET, "🥩 Gossip rebroadcast");
454 *next_rebroadcast = now + REBROADCAST_AFTER;
455 true
456 } else {
457 false
458 }
459 };
460
461 let filter = self.gossip_filter.read();
462 Box::new(move |_who, intent, _topic, mut data| {
463 if let MessageIntent::PeriodicRebroadcast = intent {
464 return do_rebroadcast;
465 }
466
467 match GossipMessage::<B, AuthorityId>::decode_all(&mut data) {
468 Ok(GossipMessage::Vote(msg)) => {
469 let round = msg.commitment.block_number;
470 let set_id = msg.commitment.validator_set_id;
471 let allowed = filter.consider_vote(round, set_id) == Consider::Accept;
472 trace!(target: LOG_TARGET, "🥩 Vote for round #{} allowed: {}", round, allowed);
473 allowed
474 },
475 Ok(GossipMessage::FinalityProof(proof)) => {
476 let (round, set_id) = proof_block_num_and_set_id::<B, AuthorityId>(&proof);
477 let allowed = filter.consider_finality_proof(round, set_id) == Consider::Accept;
478 trace!(
479 target: LOG_TARGET,
480 "🥩 Finality proof for round #{} allowed: {}",
481 round,
482 allowed
483 );
484 allowed
485 },
486 Err(_) => false,
487 }
488 })
489 }
490}
491
492#[cfg(test)]
493pub(crate) mod tests {
494 use super::*;
495 use crate::{communication::peers::PeerReport, keystore::BeefyKeystore};
496 use sc_network_test::Block;
497 use sp_application_crypto::key_types::BEEFY as BEEFY_KEY_TYPE;
498 use sp_consensus_beefy::{
499 ecdsa_crypto, known_payloads, test_utils::Keyring, Commitment, MmrRootHash, Payload,
500 SignedCommitment, VoteMessage,
501 };
502 use sp_keystore::{testing::MemoryKeystore, Keystore};
503
504 pub(crate) struct TestNetwork {
505 report_sender: futures::channel::mpsc::UnboundedSender<PeerReport>,
506 }
507
508 impl TestNetwork {
509 pub fn new() -> (Self, futures::channel::mpsc::UnboundedReceiver<PeerReport>) {
510 let (tx, rx) = futures::channel::mpsc::unbounded();
511
512 (Self { report_sender: tx }, rx)
513 }
514 }
515
516 #[async_trait::async_trait]
517 impl NetworkPeers for TestNetwork {
518 fn set_authorized_peers(&self, _: std::collections::HashSet<PeerId>) {
519 unimplemented!()
520 }
521
522 fn set_authorized_only(&self, _: bool) {
523 unimplemented!()
524 }
525
526 fn add_known_address(&self, _: PeerId, _: sc_network::Multiaddr) {
527 unimplemented!()
528 }
529
530 fn report_peer(&self, peer_id: PeerId, cost_benefit: ReputationChange) {
531 let _ = self.report_sender.unbounded_send(PeerReport { who: peer_id, cost_benefit });
532 }
533
534 fn peer_reputation(&self, _: &PeerId) -> i32 {
535 unimplemented!()
536 }
537
538 fn disconnect_peer(&self, _: PeerId, _: sc_network::ProtocolName) {
539 unimplemented!()
540 }
541
542 fn accept_unreserved_peers(&self) {
543 unimplemented!()
544 }
545
546 fn deny_unreserved_peers(&self) {
547 unimplemented!()
548 }
549
550 fn add_reserved_peer(
551 &self,
552 _: sc_network::config::MultiaddrWithPeerId,
553 ) -> Result<(), String> {
554 unimplemented!()
555 }
556
557 fn remove_reserved_peer(&self, _: PeerId) {
558 unimplemented!()
559 }
560
561 fn set_reserved_peers(
562 &self,
563 _: sc_network::ProtocolName,
564 _: std::collections::HashSet<sc_network::Multiaddr>,
565 ) -> Result<(), String> {
566 unimplemented!()
567 }
568
569 fn add_peers_to_reserved_set(
570 &self,
571 _: sc_network::ProtocolName,
572 _: std::collections::HashSet<sc_network::Multiaddr>,
573 ) -> Result<(), String> {
574 unimplemented!()
575 }
576
577 fn remove_peers_from_reserved_set(
578 &self,
579 _: sc_network::ProtocolName,
580 _: Vec<PeerId>,
581 ) -> Result<(), String> {
582 unimplemented!()
583 }
584
585 fn sync_num_connected(&self) -> usize {
586 unimplemented!()
587 }
588
589 fn peer_role(&self, _: PeerId, _: Vec<u8>) -> Option<sc_network::ObservedRole> {
590 unimplemented!()
591 }
592
593 async fn reserved_peers(&self) -> Result<Vec<PeerId>, ()> {
594 unimplemented!();
595 }
596 }
597
598 struct TestContext;
599 impl<B: sp_runtime::traits::Block> ValidatorContext<B> for TestContext {
600 fn broadcast_topic(&mut self, _topic: B::Hash, _force: bool) {
601 unimplemented!()
602 }
603
604 fn broadcast_message(&mut self, _topic: B::Hash, _message: Vec<u8>, _force: bool) {}
605
606 fn send_message(&mut self, _who: &sc_network_types::PeerId, _message: Vec<u8>) {
607 unimplemented!()
608 }
609
610 fn send_topic(&mut self, _who: &sc_network_types::PeerId, _topic: B::Hash, _force: bool) {
611 unimplemented!()
612 }
613 }
614
615 pub fn sign_commitment<BN: Encode>(
616 who: &Keyring<ecdsa_crypto::AuthorityId>,
617 commitment: &Commitment<BN>,
618 ) -> ecdsa_crypto::Signature {
619 let store = MemoryKeystore::new();
620 store.ecdsa_generate_new(BEEFY_KEY_TYPE, Some(&who.to_seed())).unwrap();
621 let beefy_keystore: BeefyKeystore<ecdsa_crypto::AuthorityId> = Some(store.into()).into();
622 beefy_keystore.sign(&who.public(), &commitment.encode()).unwrap()
623 }
624
625 fn dummy_vote(
626 block_number: u64,
627 ) -> VoteMessage<u64, ecdsa_crypto::AuthorityId, ecdsa_crypto::Signature> {
628 let payload = Payload::from_single_entry(
629 known_payloads::MMR_ROOT_ID,
630 MmrRootHash::default().encode(),
631 );
632 let commitment = Commitment { payload, block_number, validator_set_id: 0 };
633 let signature = sign_commitment(&Keyring::Alice, &commitment);
634
635 VoteMessage { commitment, id: Keyring::Alice.public(), signature }
636 }
637
638 pub fn dummy_proof(
639 block_number: u64,
640 validator_set: &ValidatorSet<ecdsa_crypto::AuthorityId>,
641 ) -> BeefyVersionedFinalityProof<Block, ecdsa_crypto::AuthorityId> {
642 let payload = Payload::from_single_entry(
643 known_payloads::MMR_ROOT_ID,
644 MmrRootHash::default().encode(),
645 );
646 let commitment = Commitment { payload, block_number, validator_set_id: validator_set.id() };
647 let signatures = validator_set
648 .validators()
649 .iter()
650 .map(|validator: &ecdsa_crypto::AuthorityId| {
651 Some(sign_commitment(
652 &Keyring::<ecdsa_crypto::AuthorityId>::from_public(validator).unwrap(),
653 &commitment,
654 ))
655 })
656 .collect();
657
658 BeefyVersionedFinalityProof::<Block, ecdsa_crypto::AuthorityId>::V1(SignedCommitment {
659 commitment,
660 signatures,
661 })
662 }
663
664 #[test]
665 fn should_validate_messages() {
666 let keys = vec![Keyring::<ecdsa_crypto::AuthorityId>::Alice.public()];
667 let validator_set =
668 ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
669
670 let (network, mut report_stream) = TestNetwork::new();
671
672 let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
673 Arc::new(Mutex::new(KnownPeers::new())),
674 Arc::new(network),
675 );
676 let sender = PeerId::random();
677 let mut context = TestContext;
678
679 let bad_encoding = b"0000000000".as_slice();
681 let expected_cost = ReputationChange::new(
682 (bad_encoding.len() as i32).saturating_mul(cost::PER_UNDECODABLE_BYTE),
683 "BEEFY: Bad packet",
684 );
685 let mut expected_report = PeerReport { who: sender, cost_benefit: expected_cost };
686 let res = gv.validate(&mut context, &sender, bad_encoding);
687 assert!(matches!(res, ValidationResult::Discard));
688 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
689
690 let vote = dummy_vote(3);
693 let encoded =
694 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote.clone()).encode();
695
696 let res = gv.validate(&mut context, &sender, &encoded);
698 assert!(matches!(res, ValidationResult::Discard));
699 assert!(report_stream.try_next().is_err());
701
702 gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set });
703 let res = gv.validate(&mut context, &sender, &encoded);
705 assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
706 expected_report.cost_benefit = benefit::VOTE_MESSAGE;
707 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
708
709 let mut bad_vote = vote.clone();
711 bad_vote.id = Keyring::Bob.public();
712 let bad_vote = GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(bad_vote).encode();
713 let res = gv.validate(&mut context, &sender, &bad_vote);
714 assert!(matches!(res, ValidationResult::Discard));
715 expected_report.cost_benefit = cost::UNKNOWN_VOTER;
716 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
717
718 gv.update_filter(GossipFilterCfg { start: 1, end: 2, validator_set: &validator_set });
720 let number = vote.commitment.block_number;
721 let set_id = vote.commitment.validator_set_id;
722 assert_eq!(gv.gossip_filter.read().consider_vote(number, set_id), Consider::RejectFuture);
723 let res = gv.validate(&mut context, &sender, &encoded);
724 assert!(matches!(res, ValidationResult::Discard));
725 expected_report.cost_benefit = cost::FUTURE_MESSAGE;
726 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
727
728 gv.update_filter(GossipFilterCfg { start: 7, end: 10, validator_set: &validator_set });
730 let number = vote.commitment.block_number;
731 let set_id = vote.commitment.validator_set_id;
732 assert_eq!(gv.gossip_filter.read().consider_vote(number, set_id), Consider::RejectPast);
733 let res = gv.validate(&mut context, &sender, &encoded);
734 assert!(matches!(res, ValidationResult::Discard));
735 expected_report.cost_benefit = cost::OUTDATED_MESSAGE;
736 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
737
738 let proof = dummy_proof(5, &validator_set);
742 let encoded_proof =
743 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
744 let res = gv.validate(&mut context, &sender, &encoded_proof);
745 assert!(matches!(res, ValidationResult::Discard));
746 expected_report.cost_benefit = cost::OUTDATED_MESSAGE;
747 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
748
749 let proof = dummy_proof(7, &validator_set);
751 let encoded_proof =
752 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
753 let res = gv.validate(&mut context, &sender, &encoded_proof);
754 assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
755 expected_report.cost_benefit = benefit::VALIDATED_PROOF;
756 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
757
758 let proof = dummy_proof(20, &validator_set);
760 let encoded_proof =
761 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
762 let res = gv.validate(&mut context, &sender, &encoded_proof);
763 assert!(matches!(res, ValidationResult::ProcessAndKeep(_)));
764 expected_report.cost_benefit = benefit::VALIDATED_PROOF;
765 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
766
767 let bad_validator_set = ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys, 1).unwrap();
769 let proof = dummy_proof(20, &bad_validator_set);
770 let encoded_proof =
771 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
772 let res = gv.validate(&mut context, &sender, &encoded_proof);
773 assert!(matches!(res, ValidationResult::Discard));
774 expected_report.cost_benefit = cost::FUTURE_MESSAGE;
775 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
776
777 let bad_validator_set =
779 ValidatorSet::<ecdsa_crypto::AuthorityId>::new(vec![Keyring::Bob.public()], 0).unwrap();
780 let proof = dummy_proof(21, &bad_validator_set);
781 let encoded_proof =
782 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
783 let res = gv.validate(&mut context, &sender, &encoded_proof);
784 assert!(matches!(res, ValidationResult::Discard));
785 expected_report.cost_benefit = cost::INVALID_PROOF;
786 expected_report.cost_benefit.value += cost::PER_SIGNATURE_CHECKED;
787 assert_eq!(report_stream.try_next().unwrap().unwrap(), expected_report);
788 }
789
790 #[test]
791 fn messages_allowed_and_expired() {
792 let keys = vec![Keyring::Alice.public()];
793 let validator_set =
794 ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
795 let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
796 Arc::new(Mutex::new(KnownPeers::new())),
797 Arc::new(TestNetwork::new().0),
798 );
799 gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set });
800 let sender = sc_network_types::PeerId::random();
801 let topic = Default::default();
802 let intent = MessageIntent::Broadcast;
803
804 gv.update_filter(GossipFilterCfg { start: 2, end: 10, validator_set: &validator_set });
806 let mut allowed = gv.message_allowed();
807 let mut expired = gv.message_expired();
808
809 assert!(!allowed(&sender, intent, &topic, &mut [0u8; 16]));
811 assert!(expired(topic, &mut [0u8; 16]));
812
813 let vote = dummy_vote(1);
815 let mut encoded_vote =
816 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
817 assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
818 assert!(expired(topic, &mut encoded_vote));
819 let proof = dummy_proof(1, &validator_set);
820 let mut encoded_proof =
821 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
822 assert!(!allowed(&sender, intent, &topic, &mut encoded_proof));
823 assert!(expired(topic, &mut encoded_proof));
824
825 let vote = dummy_vote(2);
827 let mut encoded_vote =
828 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
829 assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
830 assert!(!expired(topic, &mut encoded_vote));
831 let proof = dummy_proof(2, &validator_set);
832 let mut encoded_proof =
833 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
834 assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
835 assert!(!expired(topic, &mut encoded_proof));
836 let bad_validator_set =
838 ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 1).unwrap();
839 let proof = dummy_proof(2, &bad_validator_set);
840 let mut encoded_proof =
841 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
842 assert!(!allowed(&sender, intent, &topic, &mut encoded_proof));
843 assert!(expired(topic, &mut encoded_proof));
844
845 let vote = dummy_vote(3);
847 let mut encoded_vote =
848 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
849 assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
850 assert!(!expired(topic, &mut encoded_vote));
851 let proof = dummy_proof(3, &validator_set);
852 let mut encoded_proof =
853 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
854 assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
855 assert!(!expired(topic, &mut encoded_proof));
856
857 let vote = dummy_vote(4);
859 let mut encoded_vote =
860 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
861 assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
862 assert!(!expired(topic, &mut encoded_vote));
863 let proof = dummy_proof(4, &validator_set);
864 let mut encoded_proof =
865 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
866 assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
867 assert!(!expired(topic, &mut encoded_proof));
868
869 let vote = dummy_vote(11);
871 let mut encoded_vote =
872 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote).encode();
873 assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
874 assert!(expired(topic, &mut encoded_vote));
875 let proof = dummy_proof(11, &validator_set);
877 let mut encoded_proof =
878 GossipMessage::<Block, ecdsa_crypto::AuthorityId>::FinalityProof(proof).encode();
879 assert!(allowed(&sender, intent, &topic, &mut encoded_proof));
880 assert!(!expired(topic, &mut encoded_proof));
881 }
882
883 #[test]
884 fn messages_rebroadcast() {
885 let keys = vec![Keyring::Alice.public()];
886 let validator_set =
887 ValidatorSet::<ecdsa_crypto::AuthorityId>::new(keys.clone(), 0).unwrap();
888 let gv = GossipValidator::<Block, _, ecdsa_crypto::AuthorityId>::new(
889 Arc::new(Mutex::new(KnownPeers::new())),
890 Arc::new(TestNetwork::new().0),
891 );
892 gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set });
893 let sender = sc_network_types::PeerId::random();
894 let topic = Default::default();
895
896 let vote = dummy_vote(1);
897 let mut encoded_vote = vote.encode();
898
899 let intent = MessageIntent::PeriodicRebroadcast;
901 let mut allowed = gv.message_allowed();
902
903 assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
905
906 *gv.next_rebroadcast.lock() = Instant::now();
908
909 assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
911
912 let mut allowed = gv.message_allowed();
914 assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
916 }
917}