referrerpolicy=no-referrer-when-downgrade

bp_header_chain/justification/verification/
equivocation.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Logic for extracting equivocations from multiple GRANDPA Finality Proofs.
18
19use crate::{
20	justification::{
21		verification::{
22			Error as JustificationVerificationError, IterationFlow,
23			JustificationVerificationContext, JustificationVerifier, PrecommitError,
24			SignedPrecommit,
25		},
26		GrandpaJustification,
27	},
28	ChainWithGrandpa, FindEquivocations,
29};
30
31use bp_runtime::{BlockNumberOf, HashOf, HeaderOf};
32use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, EquivocationProof, Precommit};
33use sp_runtime::traits::Header as HeaderT;
34use sp_std::{
35	collections::{btree_map::BTreeMap, btree_set::BTreeSet},
36	prelude::*,
37	vec,
38	vec::Vec,
39};
40
41enum AuthorityVotes<Header: HeaderT> {
42	SingleVote(SignedPrecommit<Header>),
43	Equivocation(
44		finality_grandpa::Equivocation<AuthorityId, Precommit<Header>, AuthoritySignature>,
45	),
46}
47
48/// Structure that can extract equivocations from multiple GRANDPA justifications.
49pub struct EquivocationsCollector<'a, Header: HeaderT> {
50	round: u64,
51	context: &'a JustificationVerificationContext,
52
53	votes: BTreeMap<AuthorityId, AuthorityVotes<Header>>,
54}
55
56impl<'a, Header: HeaderT> EquivocationsCollector<'a, Header> {
57	/// Create a new instance of `EquivocationsCollector`.
58	pub fn new(
59		context: &'a JustificationVerificationContext,
60		base_justification: &GrandpaJustification<Header>,
61	) -> Result<Self, JustificationVerificationError> {
62		let mut checker = Self { round: base_justification.round, context, votes: BTreeMap::new() };
63
64		checker.verify_justification(
65			(base_justification.commit.target_hash, base_justification.commit.target_number),
66			checker.context,
67			base_justification,
68		)?;
69
70		Ok(checker)
71	}
72
73	/// Parse additional justifications for equivocations.
74	pub fn parse_justifications(&mut self, justifications: &[GrandpaJustification<Header>]) {
75		let round = self.round;
76		for justification in
77			justifications.iter().filter(|justification| round == justification.round)
78		{
79			// We ignore the Errors received here since we don't care if the proofs are valid.
80			// We only care about collecting equivocations.
81			let _ = self.verify_justification(
82				(justification.commit.target_hash, justification.commit.target_number),
83				self.context,
84				justification,
85			);
86		}
87	}
88
89	/// Extract the equivocation proofs that have been collected.
90	pub fn into_equivocation_proofs(self) -> Vec<EquivocationProof<Header::Hash, Header::Number>> {
91		let mut equivocations = vec![];
92		for (_authority, vote) in self.votes {
93			if let AuthorityVotes::Equivocation(equivocation) = vote {
94				equivocations.push(EquivocationProof::new(
95					self.context.authority_set_id,
96					sp_consensus_grandpa::Equivocation::Precommit(equivocation),
97				));
98			}
99		}
100
101		equivocations
102	}
103}
104
105impl<'a, Header: HeaderT> JustificationVerifier<Header> for EquivocationsCollector<'a, Header> {
106	fn process_duplicate_votes_ancestries(
107		&mut self,
108		_duplicate_votes_ancestries: Vec<usize>,
109	) -> Result<(), JustificationVerificationError> {
110		Ok(())
111	}
112
113	fn process_redundant_vote(
114		&mut self,
115		_precommit_idx: usize,
116	) -> Result<IterationFlow, PrecommitError> {
117		Ok(IterationFlow::Run)
118	}
119
120	fn process_known_authority_vote(
121		&mut self,
122		_precommit_idx: usize,
123		_signed: &SignedPrecommit<Header>,
124	) -> Result<IterationFlow, PrecommitError> {
125		Ok(IterationFlow::Run)
126	}
127
128	fn process_unknown_authority_vote(
129		&mut self,
130		_precommit_idx: usize,
131	) -> Result<(), PrecommitError> {
132		Ok(())
133	}
134
135	fn process_unrelated_ancestry_vote(
136		&mut self,
137		_precommit_idx: usize,
138	) -> Result<IterationFlow, PrecommitError> {
139		Ok(IterationFlow::Run)
140	}
141
142	fn process_invalid_signature_vote(
143		&mut self,
144		_precommit_idx: usize,
145	) -> Result<(), PrecommitError> {
146		Ok(())
147	}
148
149	fn process_valid_vote(&mut self, signed: &SignedPrecommit<Header>) {
150		match self.votes.get_mut(&signed.id) {
151			Some(vote) => match vote {
152				AuthorityVotes::SingleVote(first_vote) => {
153					if first_vote.precommit != signed.precommit {
154						*vote = AuthorityVotes::Equivocation(finality_grandpa::Equivocation {
155							round_number: self.round,
156							identity: signed.id.clone(),
157							first: (first_vote.precommit.clone(), first_vote.signature.clone()),
158							second: (signed.precommit.clone(), signed.signature.clone()),
159						});
160					}
161				},
162				AuthorityVotes::Equivocation(_) => {},
163			},
164			None => {
165				self.votes.insert(signed.id.clone(), AuthorityVotes::SingleVote(signed.clone()));
166			},
167		}
168	}
169
170	fn process_redundant_votes_ancestries(
171		&mut self,
172		_redundant_votes_ancestries: BTreeSet<Header::Hash>,
173	) -> Result<(), JustificationVerificationError> {
174		Ok(())
175	}
176}
177
178/// Helper struct for finding equivocations in GRANDPA proofs.
179pub struct GrandpaEquivocationsFinder<C>(sp_std::marker::PhantomData<C>);
180
181impl<C: ChainWithGrandpa>
182	FindEquivocations<
183		GrandpaJustification<HeaderOf<C>>,
184		JustificationVerificationContext,
185		EquivocationProof<HashOf<C>, BlockNumberOf<C>>,
186	> for GrandpaEquivocationsFinder<C>
187{
188	type Error = JustificationVerificationError;
189
190	fn find_equivocations(
191		verification_context: &JustificationVerificationContext,
192		synced_proof: &GrandpaJustification<HeaderOf<C>>,
193		source_proofs: &[GrandpaJustification<HeaderOf<C>>],
194	) -> Result<Vec<EquivocationProof<HashOf<C>, BlockNumberOf<C>>>, Self::Error> {
195		let mut equivocations_collector =
196			EquivocationsCollector::new(verification_context, synced_proof)?;
197
198		equivocations_collector.parse_justifications(source_proofs);
199
200		Ok(equivocations_collector.into_equivocation_proofs())
201	}
202}