referrerpolicy=no-referrer-when-downgrade

polkadot_node_primitives/approval/
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
17//! Types relevant for approval.
18
19/// Criteria for assignment.
20pub mod criteria;
21
22/// Time utilities for approval voting.
23pub mod time;
24
25/// A list of primitives introduced in v1.
26pub mod v1 {
27	use sp_consensus_babe as babe_primitives;
28	pub use sp_consensus_babe::{
29		Randomness, Slot, VrfPreOutput, VrfProof, VrfSignature, VrfTranscript,
30	};
31
32	use codec::{Decode, Encode};
33	use polkadot_primitives::{
34		BlockNumber, CandidateHash, CoreIndex, GroupIndex, Hash, Header, SessionIndex,
35	};
36	use sp_application_crypto::ByteArray;
37
38	/// Validators assigning to check a particular candidate are split up into tranches.
39	/// Earlier tranches of validators check first, with later tranches serving as backup.
40	pub type DelayTranche = u32;
41
42	/// A static context used to compute the Relay VRF story based on the
43	/// VRF output included in the header-chain.
44	pub const RELAY_VRF_STORY_CONTEXT: &[u8] = b"A&V RC-VRF";
45
46	/// A static context used for all relay-vrf-modulo VRFs.
47	pub const RELAY_VRF_MODULO_CONTEXT: &[u8] = b"A&V MOD";
48
49	/// A static context used for all relay-vrf-modulo VRFs.
50	pub const RELAY_VRF_DELAY_CONTEXT: &[u8] = b"A&V DELAY";
51
52	/// A static context used for transcripts indicating assigned availability core.
53	pub const ASSIGNED_CORE_CONTEXT: &[u8] = b"A&V ASSIGNED";
54
55	/// A static context associated with producing randomness for a core.
56	pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE";
57
58	/// A static context associated with producing randomness for a tranche.
59	pub const TRANCHE_RANDOMNESS_CONTEXT: &[u8] = b"A&V TRANCHE";
60
61	/// random bytes derived from the VRF submitted within the block by the
62	/// block author as a credential and used as input to approval assignment criteria.
63	#[derive(Debug, Clone, Encode, Decode, PartialEq)]
64	pub struct RelayVRFStory(pub [u8; 32]);
65
66	/// Metadata about a block which is now live in the approval protocol.
67	#[derive(Debug, Clone)]
68	pub struct BlockApprovalMeta {
69		/// The hash of the block.
70		pub hash: Hash,
71		/// The number of the block.
72		pub number: BlockNumber,
73		/// The hash of the parent block.
74		pub parent_hash: Hash,
75		/// The candidates included by the block.
76		/// Note that these are not the same as the candidates that appear within the block body.
77		pub candidates: Vec<(CandidateHash, CoreIndex, GroupIndex)>,
78		/// The consensus slot of the block.
79		pub slot: Slot,
80		/// The session of the block.
81		pub session: SessionIndex,
82		/// The vrf story.
83		pub vrf_story: RelayVRFStory,
84	}
85
86	/// Errors that can occur during the approvals protocol.
87	#[derive(Debug, thiserror::Error)]
88	#[allow(missing_docs)]
89	pub enum ApprovalError {
90		#[error("Schnorrkel signature error")]
91		SchnorrkelSignature(schnorrkel::errors::SignatureError),
92		#[error("Authority index {0} out of bounds")]
93		AuthorityOutOfBounds(usize),
94	}
95
96	/// An unsafe VRF pre-output. Provide BABE Epoch info to create a `RelayVRFStory`.
97	pub struct UnsafeVRFPreOutput {
98		vrf_pre_output: VrfPreOutput,
99		slot: Slot,
100		authority_index: u32,
101	}
102
103	impl UnsafeVRFPreOutput {
104		/// Get the slot.
105		pub fn slot(&self) -> Slot {
106			self.slot
107		}
108
109		/// Compute the randomness associated with this VRF output.
110		pub fn compute_randomness(
111			self,
112			authorities: &[(babe_primitives::AuthorityId, babe_primitives::BabeAuthorityWeight)],
113			randomness: &babe_primitives::Randomness,
114			epoch_index: u64,
115		) -> Result<RelayVRFStory, ApprovalError> {
116			let author = match authorities.get(self.authority_index as usize) {
117				None => return Err(ApprovalError::AuthorityOutOfBounds(self.authority_index as _)),
118				Some(x) => &x.0,
119			};
120
121			let pubkey = schnorrkel::PublicKey::from_bytes(author.as_slice())
122				.map_err(ApprovalError::SchnorrkelSignature)?;
123
124			let transcript =
125				sp_consensus_babe::make_vrf_transcript(randomness, self.slot, epoch_index);
126
127			let inout = self
128				.vrf_pre_output
129				.0
130				.attach_input_hash(&pubkey, transcript.0)
131				.map_err(ApprovalError::SchnorrkelSignature)?;
132			Ok(RelayVRFStory(inout.make_bytes(super::v1::RELAY_VRF_STORY_CONTEXT)))
133		}
134	}
135
136	/// Extract the slot number and relay VRF from a header.
137	///
138	/// This fails if either there is no BABE `PreRuntime` digest or
139	/// the digest has type `SecondaryPlain`, which Substrate nodes do
140	/// not produce or accept anymore.
141	pub fn babe_unsafe_vrf_info(header: &Header) -> Option<UnsafeVRFPreOutput> {
142		use babe_primitives::digests::CompatibleDigestItem;
143
144		for digest in &header.digest.logs {
145			if let Some(pre) = digest.as_babe_pre_digest() {
146				let slot = pre.slot();
147				let authority_index = pre.authority_index();
148
149				return pre.vrf_signature().map(|sig| UnsafeVRFPreOutput {
150					vrf_pre_output: sig.pre_output.clone(),
151					slot,
152					authority_index,
153				});
154			}
155		}
156
157		None
158	}
159}
160
161/// A list of primitives introduced by v2.
162pub mod v2 {
163	use codec::{Decode, Encode};
164	pub use sp_consensus_babe::{
165		Randomness, Slot, VrfPreOutput, VrfProof, VrfSignature, VrfTranscript,
166	};
167	use std::ops::BitOr;
168
169	use bitvec::{prelude::Lsb0, vec::BitVec};
170	use polkadot_primitives::{
171		CandidateIndex, CoreIndex, Hash, ValidatorIndex, ValidatorSignature,
172	};
173
174	/// A static context associated with producing randomness for a core.
175	pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE v2";
176	/// A static context associated with producing randomness for v2 multi-core assignments.
177	pub const ASSIGNED_CORE_CONTEXT: &[u8] = b"A&V ASSIGNED v2";
178	/// A static context used for all relay-vrf-modulo VRFs for v2 multi-core assignments.
179	pub const RELAY_VRF_MODULO_CONTEXT: &[u8] = b"A&V MOD v2";
180	/// A read-only bitvec wrapper
181	#[derive(Clone, Debug, Encode, Decode, Hash, PartialEq, Eq)]
182	pub struct Bitfield<T>(BitVec<u8, bitvec::order::Lsb0>, std::marker::PhantomData<T>);
183
184	/// A `read-only`, `non-zero` bitfield.
185	/// Each 1 bit identifies a candidate by the bitfield bit index.
186	pub type CandidateBitfield = Bitfield<CandidateIndex>;
187	/// A bitfield of core assignments.
188	pub type CoreBitfield = Bitfield<CoreIndex>;
189
190	/// Errors that can occur when creating and manipulating bitfields.
191	#[derive(Debug)]
192	pub enum BitfieldError {
193		/// All bits are zero.
194		NullAssignment,
195	}
196
197	/// A bit index in `Bitfield`.
198	#[cfg_attr(test, derive(PartialEq, Clone))]
199	pub struct BitIndex(pub usize);
200
201	/// Helper trait to convert primitives to `BitIndex`.
202	pub trait AsBitIndex {
203		/// Returns the index of the corresponding bit in `Bitfield`.
204		fn as_bit_index(&self) -> BitIndex;
205	}
206
207	impl<T> Bitfield<T> {
208		/// Returns the bit value at specified `index`. If `index` is greater than bitfield size,
209		/// returns `false`.
210		pub fn bit_at(&self, index: BitIndex) -> bool {
211			if self.0.len() <= index.0 {
212				false
213			} else {
214				self.0[index.0]
215			}
216		}
217
218		/// Returns number of bits.
219		pub fn len(&self) -> usize {
220			self.0.len()
221		}
222
223		/// Returns the number of 1 bits.
224		pub fn count_ones(&self) -> usize {
225			self.0.count_ones()
226		}
227
228		/// Returns the index of the first 1 bit.
229		pub fn first_one(&self) -> Option<usize> {
230			self.0.first_one()
231		}
232
233		/// Returns an iterator over inner bits.
234		pub fn iter_ones(&self) -> bitvec::slice::IterOnes<'_, u8, bitvec::order::Lsb0> {
235			self.0.iter_ones()
236		}
237
238		/// For testing purpose, we want a inner mutable ref.
239		pub fn inner_mut(&mut self) -> &mut BitVec<u8, bitvec::order::Lsb0> {
240			&mut self.0
241		}
242
243		/// Returns the inner bitfield and consumes `self`.
244		pub fn into_inner(self) -> BitVec<u8, bitvec::order::Lsb0> {
245			self.0
246		}
247	}
248
249	impl AsBitIndex for CandidateIndex {
250		fn as_bit_index(&self) -> BitIndex {
251			BitIndex(*self as usize)
252		}
253	}
254
255	impl AsBitIndex for CoreIndex {
256		fn as_bit_index(&self) -> BitIndex {
257			BitIndex(self.0 as usize)
258		}
259	}
260
261	impl AsBitIndex for usize {
262		fn as_bit_index(&self) -> BitIndex {
263			BitIndex(*self)
264		}
265	}
266
267	impl<T> From<T> for Bitfield<T>
268	where
269		T: AsBitIndex,
270	{
271		fn from(value: T) -> Self {
272			Self(
273				{
274					let mut bv = bitvec::bitvec![u8, Lsb0; 0; value.as_bit_index().0 + 1];
275					bv.set(value.as_bit_index().0, true);
276					bv
277				},
278				Default::default(),
279			)
280		}
281	}
282
283	impl<T> TryFrom<Vec<T>> for Bitfield<T>
284	where
285		T: Into<Bitfield<T>>,
286	{
287		type Error = BitfieldError;
288
289		fn try_from(mut value: Vec<T>) -> Result<Self, Self::Error> {
290			if value.is_empty() {
291				return Err(BitfieldError::NullAssignment);
292			}
293
294			let initial_bitfield =
295				value.pop().expect("Just checked above it's not empty; qed").into();
296
297			Ok(Self(
298				value.into_iter().fold(initial_bitfield.0, |initial_bitfield, element| {
299					let mut bitfield: Bitfield<T> = element.into();
300					bitfield
301						.0
302						.resize(std::cmp::max(initial_bitfield.len(), bitfield.0.len()), false);
303					bitfield.0.bitor(initial_bitfield)
304				}),
305				Default::default(),
306			))
307		}
308	}
309
310	/// The criterion which is claimed to be met by an assignment cert.
311	///
312	/// Note: SCALE codec index 2 was previously the deprecated non-compact `RelayVRFModulo`
313	/// variant and has been removed. Do not reuse index 2 for any future variant.
314	#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
315	pub enum AssignmentCertKindV2 {
316		/// Multiple assignment stories based on the VRF that authorized the relay-chain block
317		/// where the candidates were included.
318		///
319		/// The context is [`super::v2::RELAY_VRF_MODULO_CONTEXT`]
320		#[codec(index = 0)]
321		RelayVRFModuloCompact {
322			/// A bitfield representing the core indices claimed by this assignment.
323			core_bitfield: CoreBitfield,
324		},
325		/// An assignment story based on the VRF that authorized the relay-chain block where the
326		/// candidate was included combined with the index of a particular core.
327		///
328		/// The context is [`super::v1::RELAY_VRF_DELAY_CONTEXT`]
329		#[codec(index = 1)]
330		RelayVRFDelay {
331			/// The core index chosen in this cert.
332			core_index: CoreIndex,
333		},
334	}
335
336	/// A certification of assignment.
337	#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
338	pub struct AssignmentCertV2 {
339		/// The criterion which is claimed to be met by this cert.
340		pub kind: AssignmentCertKindV2,
341		/// The VRF showing the criterion is met.
342		pub vrf: VrfSignature,
343	}
344
345	/// An assignment criterion which refers to the candidate under which the assignment is
346	/// relevant by block hash.
347	#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
348	pub struct IndirectAssignmentCertV2 {
349		/// A block hash where the candidate appears.
350		pub block_hash: Hash,
351		/// The validator index.
352		pub validator: ValidatorIndex,
353		/// The cert itself.
354		pub cert: AssignmentCertV2,
355	}
356
357	/// A signed approval vote which references the candidate indirectly via the block.
358	///
359	/// In practice, we have a look-up from block hash and candidate index to candidate hash,
360	/// so this can be transformed into a `SignedApprovalVote`.
361	#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
362	pub struct IndirectSignedApprovalVoteV2 {
363		/// A block hash where the candidate appears.
364		pub block_hash: Hash,
365		/// The index of the candidate in the list of candidates fully included as-of the block.
366		pub candidate_indices: CandidateBitfield,
367		/// The validator index.
368		pub validator: ValidatorIndex,
369		/// The signature by the validator.
370		pub signature: ValidatorSignature,
371	}
372}
373
374#[cfg(test)]
375mod test {
376	use super::v2::{BitIndex, Bitfield};
377
378	use polkadot_primitives::{CandidateIndex, CoreIndex};
379
380	#[test]
381	fn test_assignment_bitfield_from_vec() {
382		let candidate_indices = vec![1u32, 7, 3, 10, 45, 8, 200, 2];
383		let max_index = *candidate_indices.iter().max().unwrap();
384		let bitfield = Bitfield::try_from(candidate_indices.clone()).unwrap();
385		let candidate_indices =
386			candidate_indices.into_iter().map(|i| BitIndex(i as usize)).collect::<Vec<_>>();
387
388		// Test 1 bits.
389		for index in candidate_indices.clone() {
390			assert!(bitfield.bit_at(index));
391		}
392
393		// Test 0 bits.
394		for index in 0..max_index {
395			if candidate_indices.contains(&BitIndex(index as usize)) {
396				continue;
397			}
398			assert!(!bitfield.bit_at(BitIndex(index as usize)));
399		}
400	}
401
402	#[test]
403	fn test_assignment_bitfield_invariant_msb() {
404		let core_indices = vec![CoreIndex(1), CoreIndex(3), CoreIndex(10), CoreIndex(20)];
405		let mut bitfield = Bitfield::try_from(core_indices.clone()).unwrap();
406		assert!(bitfield.inner_mut().pop().unwrap());
407
408		for i in 0..1024 {
409			assert!(Bitfield::try_from(CoreIndex(i)).unwrap().inner_mut().pop().unwrap());
410			assert!(Bitfield::try_from(i).unwrap().inner_mut().pop().unwrap());
411		}
412	}
413
414	#[test]
415	fn test_assignment_bitfield_basic() {
416		let bitfield = Bitfield::try_from(CoreIndex(0)).unwrap();
417		assert!(bitfield.bit_at(BitIndex(0)));
418		assert!(!bitfield.bit_at(BitIndex(1)));
419		assert_eq!(bitfield.len(), 1);
420
421		let mut bitfield = Bitfield::try_from(20 as CandidateIndex).unwrap();
422		assert!(bitfield.bit_at(BitIndex(20)));
423		assert_eq!(bitfield.inner_mut().count_ones(), 1);
424		assert_eq!(bitfield.len(), 21);
425	}
426
427	#[test]
428	fn assignment_cert_kind_v2_codec_indices_are_stable() {
429		use super::v2::{AssignmentCertKindV2, CoreBitfield};
430		use codec::Encode;
431
432		// `RelayVRFModuloCompact` must stay at codec index 0 and `RelayVRFDelay` at index 1.
433		// The deprecated non-compact `RelayVRFModulo` (previously index 2) has been removed; this
434		// guards against accidentally shifting the surviving wire-format discriminants, which would
435		// break decoding of assignments from peers that have not yet upgraded.
436		let compact = AssignmentCertKindV2::RelayVRFModuloCompact {
437			core_bitfield: CoreBitfield::try_from(vec![CoreIndex(0)]).unwrap(),
438		};
439		let delay = AssignmentCertKindV2::RelayVRFDelay { core_index: CoreIndex(0) };
440
441		assert_eq!(compact.encode()[0], 0);
442		assert_eq!(delay.encode()[0], 1);
443	}
444}