referrerpolicy=no-referrer-when-downgrade

polkadot_node_primitives/disputes/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot 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// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use std::collections::{
18	btree_map::{Entry as Bentry, Keys as Bkeys},
19	BTreeMap, BTreeSet,
20};
21
22use codec::{Decode, Encode};
23
24use sp_application_crypto::AppCrypto;
25use sp_keystore::{Error as KeystoreError, KeystorePtr};
26
27use polkadot_primitives::{
28	CandidateHash, CandidateReceiptV2 as CandidateReceipt, CompactStatement, DisputeStatement,
29	EncodeAs, InvalidDisputeStatementKind, SessionIndex, SigningContext, UncheckedSigned,
30	ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature,
31};
32
33/// `DisputeMessage` and related types.
34mod message;
35pub use message::{DisputeMessage, Error as DisputeMessageCheckError, UncheckedDisputeMessage};
36mod status;
37pub use status::{dispute_is_inactive, DisputeStatus, Timestamp, ACTIVE_DURATION_SECS};
38
39/// A checked dispute statement from an associated validator.
40#[derive(Debug, Clone)]
41pub struct SignedDisputeStatement {
42	dispute_statement: DisputeStatement,
43	candidate_hash: CandidateHash,
44	validator_public: ValidatorId,
45	validator_signature: ValidatorSignature,
46	session_index: SessionIndex,
47}
48
49/// Errors encountered while signing a dispute statement
50#[derive(Debug)]
51pub enum SignedDisputeStatementError {
52	/// Encountered a keystore error while signing
53	KeyStoreError(KeystoreError),
54	/// Could not generate signing payload
55	PayloadError,
56}
57
58/// Tracked votes on candidates, for the purposes of dispute resolution.
59#[derive(Debug, Clone)]
60pub struct CandidateVotes {
61	/// The receipt of the candidate itself.
62	pub candidate_receipt: CandidateReceipt,
63	/// Votes of validity, sorted by validator index.
64	pub valid: ValidCandidateVotes,
65	/// Votes of invalidity, sorted by validator index.
66	pub invalid: BTreeMap<ValidatorIndex, (InvalidDisputeStatementKind, ValidatorSignature)>,
67}
68
69/// Type alias for retrieving valid votes from `CandidateVotes`
70pub type ValidVoteData = (ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature));
71
72/// Type alias for retrieving invalid votes from `CandidateVotes`
73pub type InvalidVoteData = (ValidatorIndex, (InvalidDisputeStatementKind, ValidatorSignature));
74
75impl CandidateVotes {
76	/// Get the set of all validators who have votes in the set, ascending.
77	pub fn voted_indices(&self) -> BTreeSet<ValidatorIndex> {
78		let mut keys: BTreeSet<_> = self.valid.keys().cloned().collect();
79		keys.extend(self.invalid.keys().cloned());
80		keys
81	}
82}
83
84#[derive(Debug, Clone)]
85/// Valid candidate votes.
86///
87/// Prefer backing votes over other votes.
88pub struct ValidCandidateVotes {
89	votes: BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)>,
90}
91
92impl ValidCandidateVotes {
93	/// Create new empty `ValidCandidateVotes`
94	pub fn new() -> Self {
95		Self { votes: BTreeMap::new() }
96	}
97	/// Insert a vote, replacing any already existing vote.
98	///
99	/// Except, for backing votes: Backing votes are always kept, and will never get overridden.
100	/// Import of other king of `valid` votes, will be ignored if a backing vote is already
101	/// present. Any already existing `valid` vote, will be overridden by any given backing vote.
102	///
103	/// Returns: true, if the insert had any effect.
104	pub fn insert_vote(
105		&mut self,
106		validator_index: ValidatorIndex,
107		kind: ValidDisputeStatementKind,
108		sig: ValidatorSignature,
109	) -> bool {
110		match self.votes.entry(validator_index) {
111			Bentry::Vacant(vacant) => {
112				vacant.insert((kind, sig));
113				true
114			},
115			Bentry::Occupied(mut occupied) => match occupied.get().0 {
116				ValidDisputeStatementKind::BackingValid(_) |
117				ValidDisputeStatementKind::BackingSeconded(_) => false,
118				ValidDisputeStatementKind::Explicit |
119				ValidDisputeStatementKind::ApprovalChecking |
120				ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_) => {
121					occupied.insert((kind.clone(), sig));
122					kind != occupied.get().0
123				},
124			},
125		}
126	}
127
128	/// Retain any votes that match the given criteria.
129	pub fn retain<F>(&mut self, f: F)
130	where
131		F: FnMut(&ValidatorIndex, &mut (ValidDisputeStatementKind, ValidatorSignature)) -> bool,
132	{
133		self.votes.retain(f)
134	}
135
136	/// Get all the validator indices we have votes for.
137	pub fn keys(
138		&self,
139	) -> Bkeys<'_, ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> {
140		self.votes.keys()
141	}
142
143	/// Get read only direct access to underlying map.
144	pub fn raw(
145		&self,
146	) -> &BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> {
147		&self.votes
148	}
149}
150
151impl FromIterator<(ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature))>
152	for ValidCandidateVotes
153{
154	fn from_iter<T>(iter: T) -> Self
155	where
156		T: IntoIterator<Item = (ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature))>,
157	{
158		Self { votes: BTreeMap::from_iter(iter) }
159	}
160}
161
162impl From<ValidCandidateVotes>
163	for BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)>
164{
165	fn from(wrapped: ValidCandidateVotes) -> Self {
166		wrapped.votes
167	}
168}
169impl IntoIterator for ValidCandidateVotes {
170	type Item = (ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature));
171	type IntoIter = <BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> as IntoIterator>::IntoIter;
172
173	fn into_iter(self) -> Self::IntoIter {
174		self.votes.into_iter()
175	}
176}
177
178impl SignedDisputeStatement {
179	/// Create a new `SignedDisputeStatement` from information
180	/// that is available on-chain, and hence already can be trusted.
181	///
182	/// Attention: Not to be used other than with guaranteed fetches.
183	pub fn new_unchecked_from_trusted_source(
184		dispute_statement: DisputeStatement,
185		candidate_hash: CandidateHash,
186		session_index: SessionIndex,
187		validator_public: ValidatorId,
188		validator_signature: ValidatorSignature,
189	) -> Self {
190		SignedDisputeStatement {
191			dispute_statement,
192			candidate_hash,
193			validator_public,
194			validator_signature,
195			session_index,
196		}
197	}
198
199	/// Create a new `SignedDisputeStatement`, which is only possible by checking the signature.
200	pub fn new_checked(
201		dispute_statement: DisputeStatement,
202		candidate_hash: CandidateHash,
203		session_index: SessionIndex,
204		validator_public: ValidatorId,
205		validator_signature: ValidatorSignature,
206	) -> Result<Self, ()> {
207		dispute_statement
208			.check_signature(&validator_public, candidate_hash, session_index, &validator_signature)
209			.map(|_| SignedDisputeStatement {
210				dispute_statement,
211				candidate_hash,
212				validator_public,
213				validator_signature,
214				session_index,
215			})
216	}
217
218	/// Sign this statement with the given keystore and key. Pass `valid = true` to
219	/// indicate validity of the candidate, and `valid = false` to indicate invalidity.
220	pub fn sign_explicit(
221		keystore: &KeystorePtr,
222		valid: bool,
223		candidate_hash: CandidateHash,
224		session_index: SessionIndex,
225		validator_public: ValidatorId,
226	) -> Result<Option<Self>, SignedDisputeStatementError> {
227		let dispute_statement = if valid {
228			DisputeStatement::Valid(ValidDisputeStatementKind::Explicit)
229		} else {
230			DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit)
231		};
232
233		let data = dispute_statement
234			.payload_data(candidate_hash, session_index)
235			.map_err(|_| SignedDisputeStatementError::PayloadError)?;
236		let signature = keystore
237			.sr25519_sign(ValidatorId::ID, validator_public.as_ref(), &data)
238			.map_err(SignedDisputeStatementError::KeyStoreError)?
239			.map(|sig| Self {
240				dispute_statement,
241				candidate_hash,
242				validator_public,
243				validator_signature: sig.into(),
244				session_index,
245			});
246		Ok(signature)
247	}
248
249	/// Access the underlying dispute statement
250	pub fn statement(&self) -> &DisputeStatement {
251		&self.dispute_statement
252	}
253
254	/// Access the underlying candidate hash.
255	pub fn candidate_hash(&self) -> &CandidateHash {
256		&self.candidate_hash
257	}
258
259	/// Access the underlying validator public key.
260	pub fn validator_public(&self) -> &ValidatorId {
261		&self.validator_public
262	}
263
264	/// Access the underlying validator signature.
265	pub fn validator_signature(&self) -> &ValidatorSignature {
266		&self.validator_signature
267	}
268
269	/// Consume self to return the signature.
270	pub fn into_validator_signature(self) -> ValidatorSignature {
271		self.validator_signature
272	}
273
274	/// Access the underlying session index.
275	pub fn session_index(&self) -> SessionIndex {
276		self.session_index
277	}
278
279	/// Convert a unchecked backing statement to a [`SignedDisputeStatement`]
280	///
281	/// As the unchecked backing statement contains only the validator index and
282	/// not the validator public key, the public key must be passed as well,
283	/// along with the signing context.
284	///
285	/// This does signature checks again with the data provided.
286	pub fn from_backing_statement<T>(
287		backing_statement: &UncheckedSigned<T, CompactStatement>,
288		signing_context: SigningContext,
289		validator_public: ValidatorId,
290	) -> Result<Self, ()>
291	where
292		for<'a> &'a T: Into<CompactStatement>,
293		T: EncodeAs<CompactStatement>,
294	{
295		let (statement_kind, candidate_hash) = match backing_statement.unchecked_payload().into() {
296			CompactStatement::Seconded(candidate_hash) => (
297				ValidDisputeStatementKind::BackingSeconded(signing_context.parent_hash),
298				candidate_hash,
299			),
300			CompactStatement::Valid(candidate_hash) => (
301				ValidDisputeStatementKind::BackingValid(signing_context.parent_hash),
302				candidate_hash,
303			),
304		};
305
306		let dispute_statement = DisputeStatement::Valid(statement_kind);
307		Self::new_checked(
308			dispute_statement,
309			candidate_hash,
310			signing_context.session_index,
311			validator_public,
312			backing_statement.unchecked_signature().clone(),
313		)
314	}
315}
316
317/// Any invalid vote (currently only explicit).
318#[derive(Clone, Encode, Decode, Debug)]
319pub struct InvalidDisputeVote {
320	/// The voting validator index.
321	pub validator_index: ValidatorIndex,
322
323	/// The validator signature, that can be verified when constructing a
324	/// `SignedDisputeStatement`.
325	pub signature: ValidatorSignature,
326
327	/// Kind of dispute statement.
328	pub kind: InvalidDisputeStatementKind,
329}
330
331/// Any valid vote (backing, approval, explicit).
332#[derive(Clone, Encode, Decode, Debug)]
333pub struct ValidDisputeVote {
334	/// The voting validator index.
335	pub validator_index: ValidatorIndex,
336
337	/// The validator signature, that can be verified when constructing a
338	/// `SignedDisputeStatement`.
339	pub signature: ValidatorSignature,
340
341	/// Kind of dispute statement.
342	pub kind: ValidDisputeStatementKind,
343}