referrerpolicy=no-referrer-when-downgrade

sc_consensus_beefy/communication/
gossip.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use 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// Timeout for rebroadcasting messages.
43#[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	// repropagate under given topic, to the given peers, applying cost/benefit to originator.
51	Keep(H, ReputationChange),
52	// discard, applying cost/benefit to originator.
53	Discard(ReputationChange),
54	// ignore, no cost/benefit applied to originator.
55	DiscardNoReport,
56}
57
58/// An outcome of examining a message.
59#[derive(Debug, PartialEq, Clone, Copy)]
60enum Consider {
61	/// Accept the message.
62	Accept,
63	/// Message is too early. Reject.
64	RejectPast,
65	/// Message is from the future. Reject.
66	RejectFuture,
67	/// Message cannot be evaluated. Reject.
68	CannotEvaluate,
69}
70
71/// BEEFY gossip message type that gets encoded and sent on the network.
72#[derive(Debug, Encode, Decode)]
73pub(crate) enum GossipMessage<B: Block, AuthorityId: AuthorityIdBound> {
74	/// BEEFY message with commitment and single signature.
75	Vote(VoteMessage<NumberFor<B>, AuthorityId, <AuthorityId as RuntimeAppPublic>::Signature>),
76	/// BEEFY justification with commitment and signatures.
77	FinalityProof(BeefyVersionedFinalityProof<B, AuthorityId>),
78}
79
80impl<B: Block, AuthorityId: AuthorityIdBound> GossipMessage<B, AuthorityId> {
81	/// Return inner vote if this message is a Vote.
82	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	/// Return inner finality proof if this message is a FinalityProof.
93	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
101/// Gossip engine votes messages topic
102pub(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
109/// Gossip engine justifications messages topic
110pub(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	// specifies live rounds
133	inner: Option<FilterInner<B, AuthorityId>>,
134	// cache of seen valid justifications in active rounds
135	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	/// Update filter to new `start` and `set_id`.
144	fn update(&mut self, cfg: GossipFilterCfg<B, AuthorityId>) {
145		self.rounds_with_valid_proofs
146			.retain(|&round| round >= cfg.start && round <= cfg.end);
147		// only clone+overwrite big validator_set if set_id changed
148		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	/// Accept if `max(session_start, best_beefy) <= round <= best_grandpa`,
163	/// and vote `set_id` matches session set id.
164	///
165	/// Latest concluded round is still considered alive to allow proper gossiping for it.
166	fn consider_vote(&self, round: NumberFor<B>, set_id: ValidatorSetId) -> Consider {
167		self.inner
168			.as_ref()
169			.map(|f|
170				// only from current set and only [filter.start, filter.end]
171				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	/// Return true if `round` is >= than `max(session_start, best_beefy)`,
182	/// and proof `set_id` matches session set id.
183	///
184	/// Latest concluded round is still considered alive to allow proper gossiping for it.
185	fn consider_finality_proof(&self, round: NumberFor<B>, set_id: ValidatorSetId) -> Consider {
186		self.inner
187			.as_ref()
188			.map(|f|
189				// only from current set and only >= filter.start
190				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	/// Add new _known_ `round` to the set of seen valid justifications.
202	fn mark_round_as_proven(&mut self, round: NumberFor<B>) {
203		self.rounds_with_valid_proofs.insert(round);
204	}
205
206	/// Check if `round` is already part of seen valid justifications.
207	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
216/// BEEFY gossip validator
217///
218/// Validate BEEFY gossip messages and limit the number of live BEEFY voting rounds.
219///
220/// Allows messages for 'rounds >= last concluded' to flow, everything else gets
221/// rejected/expired.
222///
223///All messaging is handled in a single BEEFY global topic.
224pub(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	/// Update gossip validator filter.
253	///
254	/// Only votes for `set_id` and rounds `start <= round <= end` will be accepted.
255	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		// Verify general usefulness of the message.
285		// We are going to discard old votes right away (without verification).
286		{
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				// When we can't evaluate, it's our fault (e.g. filter not initialized yet), we
293				// discard the vote without punishing or rewarding the sending peer.
294				Consider::CannotEvaluate => return Action::DiscardNoReport,
295				Consider::Accept => {},
296			}
297
298			// ensure authority is part of the set.
299			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			// Verify general usefulness of the justification.
332			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				// When we can't evaluate, it's our fault (e.g. filter not initialized yet), we
336				// discard the proof without punishing or rewarding the sending peer.
337				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			// Verify justification signatures.
346			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				// When we can't evaluate, it's our fault (e.g. filter not initialized yet), we
365				// discard the proof without punishing or rewarding the sending peer.
366				.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		// reject message, decoding error
680		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		// verify votes validation
691
692		let vote = dummy_vote(3);
693		let encoded =
694			GossipMessage::<Block, ecdsa_crypto::AuthorityId>::Vote(vote.clone()).encode();
695
696		// filter not initialized
697		let res = gv.validate(&mut context, &sender, &encoded);
698		assert!(matches!(res, ValidationResult::Discard));
699		// nothing reported
700		assert!(report_stream.try_next().is_err());
701
702		gv.update_filter(GossipFilterCfg { start: 0, end: 10, validator_set: &validator_set });
703		// nothing in cache first time
704		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		// reject vote, voter not in validator set
710		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		// reject if the round is not GRANDPA finalized
719		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		// reject if the round is not live anymore
729		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		// now verify proofs validation
739
740		// reject old proof
741		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		// accept next proof with good set_id
750		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		// accept future proof with good set_id
759		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		// reject proof, future set_id
768		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		// reject proof, bad signatures (Bob instead of Alice)
778		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		// conclude 2
805		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		// check bad vote format
810		assert!(!allowed(&sender, intent, &topic, &mut [0u8; 16]));
811		assert!(expired(topic, &mut [0u8; 16]));
812
813		// inactive round 1 -> expired
814		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		// active round 2 -> !expired - concluded but still gossiped
826		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		// using wrong set_id -> !allowed, expired
837		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		// in progress round 3 -> !expired
846		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		// unseen round 4 -> !expired
858		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		// future round 11 -> expired
870		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		// future proofs allowed while same set_id -> allowed
876		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		// re-broadcasting only allowed at `REBROADCAST_AFTER` intervals
900		let intent = MessageIntent::PeriodicRebroadcast;
901		let mut allowed = gv.message_allowed();
902
903		// rebroadcast not allowed so soon after GossipValidator creation
904		assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
905
906		// hack the inner deadline to be `now`
907		*gv.next_rebroadcast.lock() = Instant::now();
908
909		// still not allowed on old `allowed` closure result
910		assert!(!allowed(&sender, intent, &topic, &mut encoded_vote));
911
912		// renew closure result
913		let mut allowed = gv.message_allowed();
914		// rebroadcast should be allowed now
915		assert!(allowed(&sender, intent, &topic, &mut encoded_vote));
916	}
917}