finality_grandpa/
round.rs

1// Copyright 2018-2019 Parity Technologies (UK) Ltd
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Logic for a single round of GRANDPA.
16
17mod context;
18
19use context::{Context, Vote, VoteNode};
20
21#[cfg(feature = "derive-codec")]
22use parity_scale_codec::{Decode, Encode};
23
24use crate::{
25	std::{
26		self,
27		collections::btree_map::{BTreeMap, Entry},
28		fmt,
29		vec::Vec,
30	},
31	vote_graph::VoteGraph,
32	voter_set::{VoterInfo, VoterSet},
33	weights::{VoteWeight, VoterWeight},
34};
35
36use super::{
37	BlockNumberOps, Chain, Equivocation, HistoricalVotes, Message, Precommit, Prevote,
38	SignedMessage,
39};
40
41/// The (voting) phases of a round, each corresponding to the type of
42/// votes cast in that phase.
43#[derive(PartialEq, Eq, Copy, Clone, Debug)]
44pub enum Phase {
45	/// The prevote phase in which [`Prevote`]s are cast.
46	Prevote,
47	/// The precommit phase in which [`Precommit`]s are cast.
48	Precommit,
49}
50
51/// The observed vote from a single voter.
52enum VoteMultiplicity<Vote, Signature> {
53	/// A single vote has been observed from the voter.
54	Single(Vote, Signature),
55	/// At least two votes have been observed from the voter,
56	/// i.e. an equivocation.
57	Equivocated((Vote, Signature), (Vote, Signature)),
58}
59
60impl<Vote: Eq, Signature: Eq> VoteMultiplicity<Vote, Signature> {
61	fn contains(&self, vote: &Vote, signature: &Signature) -> bool {
62		match self {
63			VoteMultiplicity::Single(v, s) => v == vote && s == signature,
64			VoteMultiplicity::Equivocated((v1, s1), (v2, s2)) =>
65				v1 == vote && s1 == signature || v2 == vote && s2 == signature,
66		}
67	}
68}
69
70struct VoteTracker<Id: Ord + Eq, Vote, Signature> {
71	votes: BTreeMap<Id, VoteMultiplicity<Vote, Signature>>,
72	current_weight: VoteWeight,
73}
74
75/// Result of adding a vote.
76pub(crate) struct AddVoteResult<'a, Vote, Signature> {
77	multiplicity: Option<&'a VoteMultiplicity<Vote, Signature>>,
78	duplicated: bool,
79}
80
81impl<Id: Ord + Eq + Clone, Vote: Clone + Eq, Signature: Clone + Eq>
82	VoteTracker<Id, Vote, Signature>
83{
84	fn new() -> Self {
85		VoteTracker { votes: BTreeMap::new(), current_weight: VoteWeight(0) }
86	}
87
88	// track a vote, returning a value containing the multiplicity of all votes from this ID
89	// and a bool indicating if the vote is duplicated.
90	// if the vote is the first equivocation, returns a value indicating
91	// it as such (the new vote is always the last in the multiplicity).
92	//
93	// if the vote is a further equivocation, it is ignored and there is nothing
94	// to do.
95	//
96	// since this struct doesn't track the round-number of votes, that must be set
97	// by the caller.
98	fn add_vote(
99		&mut self,
100		id: Id,
101		vote: Vote,
102		signature: Signature,
103		weight: VoterWeight,
104	) -> AddVoteResult<Vote, Signature> {
105		match self.votes.entry(id) {
106			Entry::Vacant(vacant) => {
107				self.current_weight = self.current_weight + weight;
108				let multiplicity = vacant.insert(VoteMultiplicity::Single(vote, signature));
109
110				AddVoteResult { multiplicity: Some(multiplicity), duplicated: false }
111			},
112			Entry::Occupied(mut occupied) => {
113				if occupied.get().contains(&vote, &signature) {
114					return AddVoteResult { multiplicity: None, duplicated: true }
115				}
116
117				// import, but ignore further equivocations.
118				let new_val = match *occupied.get_mut() {
119					VoteMultiplicity::Single(ref v, ref s) =>
120						VoteMultiplicity::Equivocated((v.clone(), s.clone()), (vote, signature)),
121					VoteMultiplicity::Equivocated(_, _) =>
122						return AddVoteResult { multiplicity: None, duplicated: false },
123				};
124
125				*occupied.get_mut() = new_val;
126
127				AddVoteResult { multiplicity: Some(&*occupied.into_mut()), duplicated: false }
128			},
129		}
130	}
131
132	// Returns all imported votes.
133	fn votes(&self) -> Vec<(Id, Vote, Signature)> {
134		let mut votes = Vec::new();
135
136		for (id, vote) in &self.votes {
137			match vote {
138				VoteMultiplicity::Single(v, s) => votes.push((id.clone(), v.clone(), s.clone())),
139				VoteMultiplicity::Equivocated((v1, s1), (v2, s2)) => {
140					votes.push((id.clone(), v1.clone(), s1.clone()));
141					votes.push((id.clone(), v2.clone(), s2.clone()));
142				},
143			}
144		}
145
146		votes
147	}
148
149	// Current vote weight and number of participants.
150	fn participation(&self) -> (VoteWeight, usize) {
151		(self.current_weight, self.votes.len())
152	}
153}
154
155/// State of the round.
156#[derive(PartialEq, Clone)]
157#[cfg_attr(any(feature = "std", test), derive(Debug))]
158#[cfg_attr(feature = "derive-codec", derive(Encode, Decode, scale_info::TypeInfo))]
159pub struct State<H, N> {
160	/// The prevote-GHOST block.
161	pub prevote_ghost: Option<(H, N)>,
162	/// The finalized block.
163	pub finalized: Option<(H, N)>,
164	/// The new round-estimate.
165	pub estimate: Option<(H, N)>,
166	/// Whether the round is completable.
167	pub completable: bool,
168}
169
170impl<H: Clone, N: Clone> State<H, N> {
171	/// Genesis state.
172	pub fn genesis(genesis: (H, N)) -> Self {
173		State {
174			prevote_ghost: Some(genesis.clone()),
175			finalized: Some(genesis.clone()),
176			estimate: Some(genesis),
177			completable: true,
178		}
179	}
180}
181
182/// Parameters for starting a round.
183pub struct RoundParams<Id: Ord + Eq, H, N> {
184	/// The round number for votes.
185	pub round_number: u64,
186	/// Actors and weights in the round.
187	pub voters: VoterSet<Id>,
188	/// The base block to build on.
189	pub base: (H, N),
190}
191
192/// Stores data for a round.
193pub struct Round<Id: Ord + Eq, H: Ord + Eq, N, Signature> {
194	round_number: u64,
195	context: Context<Id>,
196	graph: VoteGraph<H, N, VoteNode>, // DAG of blocks which have been voted on.
197	prevote: VoteTracker<Id, Prevote<H, N>, Signature>, // tracks prevotes that have been counted
198	precommit: VoteTracker<Id, Precommit<H, N>, Signature>, // tracks precommits
199	historical_votes: HistoricalVotes<H, N, Signature, Id>,
200	prevote_ghost: Option<(H, N)>,   // current memoized prevote-GHOST block
201	precommit_ghost: Option<(H, N)>, // current memoized precommit-GHOST block
202	finalized: Option<(H, N)>,       // best finalized block in this round.
203	estimate: Option<(H, N)>,        // current memoized round-estimate
204	completable: bool,               // whether the round is completable
205}
206
207/// Result of importing a Prevote or Precommit.
208pub(crate) struct ImportResult<Id, P, Signature> {
209	/// Indicates if the voter is part of the voter set.
210	pub(crate) valid_voter: bool,
211	/// Indicates if the vote is duplicated.
212	pub(crate) duplicated: bool,
213	/// An equivocation proof, if the vote is an equivocation.
214	pub(crate) equivocation: Option<Equivocation<Id, P, Signature>>,
215}
216
217impl<Id, P, Signature> Default for ImportResult<Id, P, Signature> {
218	fn default() -> Self {
219		ImportResult { valid_voter: false, duplicated: false, equivocation: None }
220	}
221}
222
223impl<Id, H, N, Signature> Round<Id, H, N, Signature>
224where
225	Id: Ord + Clone + Eq + fmt::Debug,
226	H: Ord + Clone + Eq + Ord + fmt::Debug,
227	N: Copy + fmt::Debug + BlockNumberOps,
228	Signature: Eq + Clone,
229{
230	/// Create a new round accumulator for given round number and with given weight.
231	pub fn new(round_params: RoundParams<Id, H, N>) -> Self {
232		let (base_hash, base_number) = round_params.base;
233
234		Round {
235			round_number: round_params.round_number,
236			context: Context::new(round_params.voters),
237			graph: VoteGraph::new(base_hash, base_number, VoteNode::default()),
238			prevote: VoteTracker::new(),
239			precommit: VoteTracker::new(),
240			historical_votes: HistoricalVotes::new(),
241			prevote_ghost: None,
242			precommit_ghost: None,
243			finalized: None,
244			estimate: None,
245			completable: false,
246		}
247	}
248
249	/// Return the round number.
250	pub fn number(&self) -> u64 {
251		self.round_number
252	}
253
254	/// Import a prevote. Returns an equivocation proof, if the vote is an equivocation,
255	/// and a bool indicating if the vote is duplicated (see `ImportResult`).
256	///
257	/// Ignores duplicate prevotes (not equivocations).
258	#[cfg_attr(not(feature = "std"), allow(unused))]
259	pub(crate) fn import_prevote<C: Chain<H, N>>(
260		&mut self,
261		chain: &C,
262		prevote: Prevote<H, N>,
263		signer: Id,
264		signature: Signature,
265	) -> Result<ImportResult<Id, Prevote<H, N>, Signature>, crate::Error> {
266		let mut import_result = ImportResult::default();
267
268		let info = match self.context.voters().get(&signer) {
269			Some(info) => info.clone(),
270			None => return Ok(import_result),
271		};
272
273		import_result.valid_voter = true;
274		let weight = info.weight();
275
276		let equivocation = {
277			let multiplicity = match self.prevote.add_vote(
278				signer.clone(),
279				prevote.clone(),
280				signature.clone(),
281				weight,
282			) {
283				AddVoteResult { multiplicity: Some(m), .. } => m,
284				AddVoteResult { duplicated, .. } => {
285					import_result.duplicated = duplicated;
286					return Ok(import_result)
287				},
288			};
289			let round_number = self.round_number;
290
291			match multiplicity {
292				VoteMultiplicity::Single(single_vote, _) => {
293					let vote = Vote::new(&info, Phase::Prevote);
294
295					self.graph.insert(
296						single_vote.target_hash.clone(),
297						single_vote.target_number,
298						vote,
299						chain,
300					)?;
301
302					// Push the vote into HistoricalVotes.
303					let message = Message::Prevote(prevote);
304					let signed_message = SignedMessage { id: signer, signature, message };
305					self.historical_votes.push_vote(signed_message);
306
307					None
308				},
309				VoteMultiplicity::Equivocated(ref first, ref second) => {
310					// mark the equivocator as such. no need to "undo" the first vote.
311					self.context.equivocated(&info, Phase::Prevote);
312
313					// Push the vote into HistoricalVotes.
314					let message = Message::Prevote(prevote);
315					let signed_message = SignedMessage { id: signer.clone(), signature, message };
316					self.historical_votes.push_vote(signed_message);
317
318					Some(Equivocation {
319						round_number,
320						identity: signer,
321						first: first.clone(),
322						second: second.clone(),
323					})
324				},
325			}
326		};
327
328		// update prevote-GHOST
329		let threshold = self.threshold();
330		if self.prevote.current_weight >= threshold {
331			self.prevote_ghost = self.graph.find_ghost(self.prevote_ghost.take(), |v| {
332				self.context.weight(v, Phase::Prevote) >= threshold
333			});
334		}
335
336		self.update();
337		import_result.equivocation = equivocation;
338		Ok(import_result)
339	}
340
341	/// Import a precommit. Returns an equivocation proof, if the vote is an
342	/// equivocation, and a bool indicating if the vote is duplicated (see `ImportResult`).
343	///
344	/// Ignores duplicate precommits (not equivocations).
345	pub(crate) fn import_precommit<C: Chain<H, N>>(
346		&mut self,
347		chain: &C,
348		precommit: Precommit<H, N>,
349		signer: Id,
350		signature: Signature,
351	) -> Result<ImportResult<Id, Precommit<H, N>, Signature>, crate::Error> {
352		let mut import_result = ImportResult::default();
353
354		let info = match self.context.voters().get(&signer) {
355			Some(info) => info.clone(),
356			None => return Ok(import_result),
357		};
358		import_result.valid_voter = true;
359		let weight = info.weight();
360
361		let equivocation = {
362			let multiplicity = match self.precommit.add_vote(
363				signer.clone(),
364				precommit.clone(),
365				signature.clone(),
366				weight,
367			) {
368				AddVoteResult { multiplicity: Some(m), .. } => m,
369				AddVoteResult { duplicated, .. } => {
370					import_result.duplicated = duplicated;
371					return Ok(import_result)
372				},
373			};
374
375			let round_number = self.round_number;
376
377			match multiplicity {
378				VoteMultiplicity::Single(single_vote, _) => {
379					let vote = Vote::new(&info, Phase::Precommit);
380
381					self.graph.insert(
382						single_vote.target_hash.clone(),
383						single_vote.target_number,
384						vote,
385						chain,
386					)?;
387
388					let message = Message::Precommit(precommit);
389					let signed_message = SignedMessage { id: signer, signature, message };
390					self.historical_votes.push_vote(signed_message);
391
392					None
393				},
394				VoteMultiplicity::Equivocated(ref first, ref second) => {
395					// mark the equivocator as such. no need to "undo" the first vote.
396					self.context.equivocated(&info, Phase::Precommit);
397
398					// Push the vote into HistoricalVotes.
399					let message = Message::Precommit(precommit);
400					let signed_message = SignedMessage { id: signer.clone(), signature, message };
401					self.historical_votes.push_vote(signed_message);
402
403					Some(Equivocation {
404						round_number,
405						identity: signer,
406						first: first.clone(),
407						second: second.clone(),
408					})
409				},
410			}
411		};
412
413		self.update();
414		import_result.equivocation = equivocation;
415		Ok(import_result)
416	}
417
418	/// Return the current state.
419	pub fn state(&self) -> State<H, N> {
420		State {
421			prevote_ghost: self.prevote_ghost.clone(),
422			finalized: self.finalized.clone(),
423			estimate: self.estimate.clone(),
424			completable: self.completable,
425		}
426	}
427
428	/// Compute and cache the precommit-GHOST.
429	pub fn precommit_ghost(&mut self) -> Option<(H, N)> {
430		// update precommit-GHOST
431		let threshold = self.threshold();
432		if self.precommit.current_weight >= threshold {
433			self.precommit_ghost = self.graph.find_ghost(self.precommit_ghost.take(), |v| {
434				self.context.weight(v, Phase::Precommit) >= threshold
435			});
436		}
437
438		self.precommit_ghost.clone()
439	}
440
441	/// Returns an iterator of all precommits targeting the finalized hash.
442	///
443	/// Only returns `None` if no block has been finalized in this round.
444	pub fn finalizing_precommits<'a, C: 'a + Chain<H, N>>(
445		&'a mut self,
446		chain: &'a C,
447	) -> Option<impl Iterator<Item = crate::SignedPrecommit<H, N, Signature, Id>> + 'a> {
448		struct YieldVotes<'b, V: 'b, S: 'b> {
449			yielded: usize,
450			multiplicity: &'b VoteMultiplicity<V, S>,
451		}
452
453		impl<'b, V: 'b + Clone, S: 'b + Clone> Iterator for YieldVotes<'b, V, S> {
454			type Item = (V, S);
455
456			fn next(&mut self) -> Option<(V, S)> {
457				match self.multiplicity {
458					VoteMultiplicity::Single(ref v, ref s) =>
459						if self.yielded == 0 {
460							self.yielded += 1;
461							Some((v.clone(), s.clone()))
462						} else {
463							None
464						},
465					VoteMultiplicity::Equivocated(ref a, ref b) => {
466						let res = match self.yielded {
467							0 => Some(a.clone()),
468							1 => Some(b.clone()),
469							_ => None,
470						};
471
472						self.yielded += 1;
473						res
474					},
475				}
476			}
477		}
478
479		let (f_hash, _f_num) = self.finalized.clone()?;
480		let find_valid_precommits = self
481			.precommit
482			.votes
483			.iter()
484			.filter(move |&(_id, multiplicity)| {
485				if let VoteMultiplicity::Single(ref v, _) = *multiplicity {
486					// if there is a single vote from this voter, we only include it
487					// if it branches off of the target.
488					chain.is_equal_or_descendent_of(f_hash.clone(), v.target_hash.clone())
489				} else {
490					// equivocations count for everything, so we always include them.
491					true
492				}
493			})
494			.flat_map(|(id, multiplicity)| {
495				let yield_votes = YieldVotes { yielded: 0, multiplicity };
496
497				yield_votes.map(move |(v, s)| crate::SignedPrecommit {
498					precommit: v,
499					signature: s,
500					id: id.clone(),
501				})
502			});
503
504		Some(find_valid_precommits)
505	}
506
507	// update the round-estimate and whether the round is completable.
508	fn update(&mut self) {
509		let threshold = self.threshold();
510
511		if self.prevote.current_weight < threshold {
512			return
513		}
514
515		let (g_hash, g_num) = match self.prevote_ghost.clone() {
516			None => return,
517			Some(x) => x,
518		};
519
520		let ctx = &self.context;
521
522		// anything new finalized? finalized blocks are those which have both
523		// 2/3+ prevote and precommit weight.
524		let current_precommits = self.precommit.current_weight;
525		if current_precommits >= self.threshold() {
526			self.finalized = self.graph.find_ancestor(g_hash.clone(), g_num, |v| {
527				ctx.weight(v, Phase::Precommit) >= threshold
528			});
529		};
530
531		// figuring out whether a block can still be committed for is
532		// not straightforward because we have to account for all possible future
533		// equivocations and thus cannot discount weight from validators who
534		// have already voted.
535		let possible_to_precommit = {
536			// find how many more equivocations we could still get.
537			//
538			// it is only important to consider the voters whose votes
539			// we have already seen, because we are assuming any votes we
540			// haven't seen will target this block.
541			let tolerated_equivocations = ctx.voters().total_weight() - threshold;
542			let current_equivocations = ctx.equivocation_weight(Phase::Precommit);
543			let additional_equiv = tolerated_equivocations - current_equivocations;
544			let remaining_commit_votes =
545				ctx.voters().total_weight() - self.precommit.current_weight;
546
547			move |node: &VoteNode| {
548				// total precommits for this block, including equivocations.
549				let precommitted_for = ctx.weight(node, Phase::Precommit);
550
551				// equivocations we could still get are out of those who
552				// have already voted, but not on this block.
553				let possible_equivocations =
554					std::cmp::min(current_precommits - precommitted_for, additional_equiv);
555
556				// all the votes already applied on this block,
557				// assuming all remaining actors commit to this block,
558				// and that we get further equivocations
559				let full_possible_weight =
560					precommitted_for + remaining_commit_votes + possible_equivocations;
561
562				full_possible_weight >= threshold
563			}
564		};
565
566		// until we have threshold precommits, any new block could get supermajority
567		// precommits because there are at least f + 1 precommits remaining and then
568		// f equivocations.
569		//
570		// once it's at least that level, we only need to consider blocks
571		// already referenced in the graph, because no new leaf nodes
572		// could ever have enough precommits.
573		//
574		// the round-estimate is the highest block in the chain with head
575		// `prevote_ghost` that could have supermajority-commits.
576		if self.precommit.current_weight >= threshold {
577			self.estimate = self.graph.find_ancestor(g_hash.clone(), g_num, possible_to_precommit);
578		} else {
579			self.estimate = Some((g_hash, g_num));
580			return
581		}
582
583		self.completable = self.estimate.clone().map_or(false, |(b_hash, b_num)| {
584			b_hash != g_hash || {
585				// round-estimate is the same as the prevote-ghost.
586				// this round is still completable if no further blocks
587				// could have commit-supermajority.
588				self.graph
589					.find_ghost(Some((b_hash, b_num)), possible_to_precommit)
590					.map_or(true, |x| x == (g_hash, g_num))
591			}
592		})
593	}
594
595	/// Fetch the "round-estimate": the best block which might have been finalized
596	/// in this round.
597	///
598	/// Returns `None` when new new blocks could have been finalized in this round,
599	/// according to our estimate.
600	pub fn estimate(&self) -> Option<&(H, N)> {
601		self.estimate.as_ref()
602	}
603
604	/// Fetch the most recently finalized block.
605	pub fn finalized(&self) -> Option<&(H, N)> {
606		self.finalized.as_ref()
607	}
608
609	/// Returns `true` when the round is completable.
610	///
611	/// This is the case when the round-estimate is an ancestor of the prevote-ghost head,
612	/// or when they are the same block _and_ none of its children could possibly have
613	/// enough precommits.
614	pub fn completable(&self) -> bool {
615		self.completable
616	}
617
618	/// Threshold weight for supermajority.
619	pub fn threshold(&self) -> VoterWeight {
620		self.context.voters().threshold()
621	}
622
623	/// Return the round base.
624	pub fn base(&self) -> (H, N) {
625		self.graph.base()
626	}
627
628	/// Return the round voters and weights.
629	pub fn voters(&self) -> &VoterSet<Id> {
630		self.context.voters()
631	}
632
633	/// Return the primary voter of the round.
634	pub fn primary_voter(&self) -> (&Id, &VoterInfo) {
635		self.context.voters().nth_mod(self.round_number as usize)
636	}
637
638	/// Get the current weight and number of voters who have participated in prevoting.
639	pub fn prevote_participation(&self) -> (VoteWeight, usize) {
640		self.prevote.participation()
641	}
642
643	/// Get the current weight and number of voters who have participated in precommitting.
644	pub fn precommit_participation(&self) -> (VoteWeight, usize) {
645		self.precommit.participation()
646	}
647
648	/// Return all imported prevotes.
649	pub fn prevotes(&self) -> Vec<(Id, Prevote<H, N>, Signature)> {
650		self.prevote.votes()
651	}
652
653	/// Return all imported precommits.
654	pub fn precommits(&self) -> Vec<(Id, Precommit<H, N>, Signature)> {
655		self.precommit.votes()
656	}
657
658	/// Return all votes for the round (prevotes and precommits), sorted by
659	/// imported order and indicating the indices where we voted. At most two
660	/// prevotes and two precommits per voter are present, further equivocations
661	/// are not stored (as they are redundant).
662	pub fn historical_votes(&self) -> &HistoricalVotes<H, N, Signature, Id> {
663		&self.historical_votes
664	}
665
666	/// Set the number of prevotes and precommits received at the moment of prevoting.
667	/// It should be called inmediatly after prevoting.
668	pub fn set_prevoted_index(&mut self) {
669		self.historical_votes.set_prevoted_idx()
670	}
671
672	/// Set the number of prevotes and precommits received at the moment of precommiting.
673	/// It should be called inmediatly after precommiting.
674	pub fn set_precommitted_index(&mut self) {
675		self.historical_votes.set_precommitted_idx()
676	}
677
678	/// Get the number of prevotes and precommits received at the moment of prevoting.
679	/// Returns None if the prevote wasn't realized.
680	pub fn prevoted_index(&self) -> Option<u64> {
681		self.historical_votes.prevote_idx
682	}
683
684	/// Get the number of prevotes and precommits received at the moment of precommiting.
685	/// Returns None if the precommit wasn't realized.
686	pub fn precommitted_index(&self) -> Option<u64> {
687		self.historical_votes.precommit_idx
688	}
689}
690
691#[cfg(test)]
692mod tests {
693	use super::*;
694	use crate::testing::chain::{DummyChain, GENESIS_HASH};
695
696	fn voters() -> VoterSet<&'static str> {
697		VoterSet::new([("Alice", 4), ("Bob", 7), ("Eve", 3)].iter().cloned()).expect("nonempty")
698	}
699
700	#[derive(PartialEq, Eq, Clone, Debug)]
701	struct Signature(&'static str);
702
703	#[test]
704	fn estimate_is_valid() {
705		let mut chain = DummyChain::new();
706		chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
707		chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
708		chain.push_blocks("F", &["FA", "FB", "FC"]);
709
710		let mut round =
711			Round::new(RoundParams { round_number: 1, voters: voters(), base: ("C", 4) });
712
713		round
714			.import_prevote(&chain, Prevote::new("FC", 10), "Alice", Signature("Alice"))
715			.unwrap();
716
717		round
718			.import_prevote(&chain, Prevote::new("ED", 10), "Bob", Signature("Bob"))
719			.unwrap();
720
721		assert_eq!(round.prevote_ghost, Some(("E", 6)));
722		assert_eq!(round.estimate(), Some(&("E", 6)));
723		assert!(!round.completable());
724
725		round
726			.import_prevote(&chain, Prevote::new("F", 7), "Eve", Signature("Eve"))
727			.unwrap();
728
729		assert_eq!(round.prevote_ghost, Some(("E", 6)));
730		assert_eq!(round.estimate(), Some(&("E", 6)));
731	}
732
733	#[test]
734	fn finalization() {
735		let mut chain = DummyChain::new();
736		chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
737		chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
738		chain.push_blocks("F", &["FA", "FB", "FC"]);
739
740		let mut round =
741			Round::new(RoundParams { round_number: 1, voters: voters(), base: ("C", 4) });
742
743		round
744			.import_precommit(&chain, Precommit::new("FC", 10), "Alice", Signature("Alice"))
745			.unwrap();
746
747		round
748			.import_precommit(&chain, Precommit::new("ED", 10), "Bob", Signature("Bob"))
749			.unwrap();
750
751		assert_eq!(round.finalized, None);
752
753		// import some prevotes.
754		{
755			round
756				.import_prevote(&chain, Prevote::new("FC", 10), "Alice", Signature("Alice"))
757				.unwrap();
758
759			round
760				.import_prevote(&chain, Prevote::new("ED", 10), "Bob", Signature("Bob"))
761				.unwrap();
762
763			round
764				.import_prevote(&chain, Prevote::new("EA", 7), "Eve", Signature("Eve"))
765				.unwrap();
766
767			assert_eq!(round.finalized, Some(("E", 6)));
768		}
769
770		round
771			.import_precommit(&chain, Precommit::new("EA", 7), "Eve", Signature("Eve"))
772			.unwrap();
773
774		assert_eq!(round.finalized, Some(("EA", 7)));
775	}
776
777	#[test]
778	fn equivocate_does_not_double_count() {
779		let mut chain = DummyChain::new();
780		chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
781		chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
782		chain.push_blocks("F", &["FA", "FB", "FC"]);
783
784		let mut round =
785			Round::new(RoundParams { round_number: 1, voters: voters(), base: ("C", 4) });
786
787		// first prevote by eve
788		assert!(round
789			.import_prevote(
790				&chain,
791				Prevote::new("FC", 10),
792				"Eve", // 3 on F, E
793				Signature("Eve-1"),
794			)
795			.unwrap()
796			.equivocation
797			.is_none());
798
799		assert!(round.prevote_ghost.is_none());
800
801		// second prevote by eve: comes with equivocation proof
802		assert!(round
803			.import_prevote(
804				&chain,
805				Prevote::new("ED", 10),
806				"Eve", // still 3 on E
807				Signature("Eve-2"),
808			)
809			.unwrap()
810			.equivocation
811			.is_some());
812
813		// third prevote: returns nothing.
814		assert!(round
815			.import_prevote(
816				&chain,
817				Prevote::new("F", 7),
818				"Eve", // still 3 on F and E
819				Signature("Eve-2"),
820			)
821			.unwrap()
822			.equivocation
823			.is_none());
824
825		// three eves together would be enough.
826
827		assert!(round.prevote_ghost.is_none());
828
829		assert!(round
830			.import_prevote(
831				&chain,
832				Prevote::new("FA", 8),
833				"Bob", // add 7 to FA and you get FA.
834				Signature("Bob-1"),
835			)
836			.unwrap()
837			.equivocation
838			.is_none());
839
840		assert_eq!(round.prevote_ghost, Some(("FA", 8)));
841	}
842
843	#[test]
844	fn historical_votes_works() {
845		let mut chain = DummyChain::new();
846		chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
847		chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
848		chain.push_blocks("F", &["FA", "FB", "FC"]);
849
850		let mut round =
851			Round::new(RoundParams { round_number: 1, voters: voters(), base: ("C", 4) });
852
853		round
854			.import_prevote(&chain, Prevote::new("FC", 10), "Alice", Signature("Alice"))
855			.unwrap();
856
857		round.set_prevoted_index();
858
859		round
860			.import_prevote(&chain, Prevote::new("EA", 7), "Eve", Signature("Eve"))
861			.unwrap();
862
863		round
864			.import_precommit(&chain, Precommit::new("EA", 7), "Eve", Signature("Eve"))
865			.unwrap();
866
867		round
868			.import_prevote(&chain, Prevote::new("EC", 10), "Alice", Signature("Alice"))
869			.unwrap();
870
871		round.set_precommitted_index();
872
873		assert_eq!(
874			round.historical_votes(),
875			&HistoricalVotes::new_with(
876				vec![
877					SignedMessage {
878						message: Message::Prevote(Prevote { target_hash: "FC", target_number: 10 }),
879						signature: Signature("Alice"),
880						id: "Alice"
881					},
882					SignedMessage {
883						message: Message::Prevote(Prevote { target_hash: "EA", target_number: 7 }),
884						signature: Signature("Eve"),
885						id: "Eve"
886					},
887					SignedMessage {
888						message: Message::Precommit(Precommit {
889							target_hash: "EA",
890							target_number: 7
891						}),
892						signature: Signature("Eve"),
893						id: "Eve"
894					},
895					SignedMessage {
896						message: Message::Prevote(Prevote { target_hash: "EC", target_number: 10 }),
897						signature: Signature("Alice"),
898						id: "Alice"
899					},
900				],
901				Some(1),
902				Some(4),
903			)
904		);
905	}
906}