referrerpolicy=no-referrer-when-downgrade

sc_consensus_babe/
verification.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
19//! Verification for BABE headers.
20use crate::{
21	authorship::{calculate_primary_threshold, secondary_slot_author},
22	babe_err, find_pre_digest, BlockT, Epoch, Error, AUTHORING_SCORE_LENGTH,
23	AUTHORING_SCORE_VRF_CONTEXT, LOG_TARGET,
24};
25use log::{debug, trace};
26use sc_consensus_epochs::Epoch as EpochT;
27use sc_consensus_slots::CheckedHeader;
28use sp_consensus_babe::{
29	digests::{
30		CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest,
31		SecondaryVRFPreDigest,
32	},
33	make_vrf_sign_data, AuthorityPair, AuthoritySignature,
34};
35use sp_consensus_slots::Slot;
36use sp_core::{
37	crypto::{VrfPublic, Wraps},
38	Pair,
39};
40use sp_runtime::{traits::Header, DigestItem};
41
42/// BABE verification parameters
43pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
44	/// The header being verified.
45	pub(super) header: B::Header,
46	/// The pre-digest of the header being verified. this is optional - if prior
47	/// verification code had to read it, it can be included here to avoid duplicate
48	/// work.
49	pub(super) pre_digest: Option<PreDigest>,
50	/// The slot number of the current time.
51	pub(super) slot_now: Slot,
52	/// Epoch descriptor of the epoch this block _should_ be under, if it's valid.
53	pub(super) epoch: &'a Epoch,
54}
55
56/// Check a header has been signed by the right key. If the slot is too far in
57/// the future, an error will be returned. If successful, returns the pre-header
58/// and the digest item containing the seal.
59///
60/// The seal must be the last digest.  Otherwise, the whole header is considered
61/// unsigned.  This is required for security and must not be changed.
62///
63/// This digest item will always return `Some` when used with `as_babe_pre_digest`.
64///
65/// The given header can either be from a primary or secondary slot assignment,
66/// with each having different validation logic.
67pub(super) fn check_header<B: BlockT + Sized>(
68	params: VerificationParams<B>,
69) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo>, Error<B>> {
70	let VerificationParams { mut header, pre_digest, slot_now, epoch } = params;
71
72	let pre_digest = pre_digest.map(Ok).unwrap_or_else(|| find_pre_digest::<B>(&header))?;
73
74	trace!(target: LOG_TARGET, "Checking header");
75	let seal = header
76		.digest_mut()
77		.pop()
78		.ok_or_else(|| babe_err(Error::HeaderUnsealed(header.hash())))?;
79
80	let sig = seal
81		.as_babe_seal()
82		.ok_or_else(|| babe_err(Error::HeaderBadSeal(header.hash())))?;
83
84	// the pre-hash of the header doesn't include the seal
85	// and that's what we sign
86	let pre_hash = header.hash();
87
88	if pre_digest.slot() > slot_now {
89		header.digest_mut().push(seal);
90		return Ok(CheckedHeader::Deferred(header, pre_digest.slot()))
91	}
92
93	match &pre_digest {
94		PreDigest::Primary(primary) => {
95			debug!(
96				target: LOG_TARGET,
97				"Verifying primary block #{} at slot: {}",
98				header.number(),
99				primary.slot,
100			);
101
102			check_primary_header::<B>(pre_hash, primary, sig, epoch, epoch.config.c)?;
103		},
104		PreDigest::SecondaryPlain(secondary)
105			if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() =>
106		{
107			debug!(
108				target: LOG_TARGET,
109				"Verifying secondary plain block #{} at slot: {}",
110				header.number(),
111				secondary.slot,
112			);
113
114			check_secondary_plain_header::<B>(pre_hash, secondary, sig, epoch)?;
115		},
116		PreDigest::SecondaryVRF(secondary)
117			if epoch.config.allowed_slots.is_secondary_vrf_slots_allowed() =>
118		{
119			debug!(
120				target: LOG_TARGET,
121				"Verifying secondary VRF block #{} at slot: {}",
122				header.number(),
123				secondary.slot,
124			);
125
126			check_secondary_vrf_header::<B>(pre_hash, secondary, sig, epoch)?;
127		},
128		_ => return Err(babe_err(Error::SecondarySlotAssignmentsDisabled)),
129	}
130
131	let info = VerifiedHeaderInfo { seal };
132	Ok(CheckedHeader::Checked(header, info))
133}
134
135pub(super) struct VerifiedHeaderInfo {
136	pub(super) seal: DigestItem,
137}
138
139/// Check a primary slot proposal header. We validate that the given header is
140/// properly signed by the expected authority, and that the contained VRF proof
141/// is valid. Additionally, the weight of this block must increase compared to
142/// its parent since it is a primary block.
143fn check_primary_header<B: BlockT + Sized>(
144	pre_hash: B::Hash,
145	pre_digest: &PrimaryPreDigest,
146	signature: AuthoritySignature,
147	epoch: &Epoch,
148	c: (u64, u64),
149) -> Result<(), Error<B>> {
150	let authority_id = &epoch
151		.authorities
152		.get(pre_digest.authority_index as usize)
153		.ok_or(Error::SlotAuthorNotFound)?
154		.0;
155	let mut epoch_index = epoch.epoch_index;
156
157	if epoch.end_slot() <= pre_digest.slot {
158		// Slot doesn't strictly belong to this epoch, create a clone with fixed values.
159		epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index;
160	}
161
162	if !AuthorityPair::verify(&signature, pre_hash, authority_id) {
163		return Err(babe_err(Error::BadSignature(pre_hash)))
164	}
165
166	let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index);
167
168	if !authority_id.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) {
169		return Err(babe_err(Error::VrfVerificationFailed))
170	}
171
172	let threshold =
173		calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize);
174
175	let score = authority_id
176		.as_inner_ref()
177		.make_bytes::<AUTHORING_SCORE_LENGTH>(
178			AUTHORING_SCORE_VRF_CONTEXT,
179			&data.as_ref(),
180			&pre_digest.vrf_signature.pre_output,
181		)
182		.map(u128::from_le_bytes)
183		.map_err(|_| babe_err(Error::VrfVerificationFailed))?;
184
185	if score >= threshold {
186		return Err(babe_err(Error::VrfThresholdExceeded(threshold)))
187	}
188
189	Ok(())
190}
191
192/// Check a secondary slot proposal header. We validate that the given header is
193/// properly signed by the expected authority, which we have a deterministic way
194/// of computing. Additionally, the weight of this block must stay the same
195/// compared to its parent since it is a secondary block.
196fn check_secondary_plain_header<B: BlockT>(
197	pre_hash: B::Hash,
198	pre_digest: &SecondaryPlainPreDigest,
199	signature: AuthoritySignature,
200	epoch: &Epoch,
201) -> Result<(), Error<B>> {
202	// check the signature is valid under the expected authority and chain state.
203	let expected_author =
204		secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
205			.ok_or(Error::NoSecondaryAuthorExpected)?;
206
207	let author = &epoch
208		.authorities
209		.get(pre_digest.authority_index as usize)
210		.ok_or(Error::SlotAuthorNotFound)?
211		.0;
212
213	if expected_author != author {
214		return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()))
215	}
216
217	if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
218		return Err(Error::BadSignature(pre_hash))
219	}
220
221	Ok(())
222}
223
224/// Check a secondary VRF slot proposal header.
225fn check_secondary_vrf_header<B: BlockT>(
226	pre_hash: B::Hash,
227	pre_digest: &SecondaryVRFPreDigest,
228	signature: AuthoritySignature,
229	epoch: &Epoch,
230) -> Result<(), Error<B>> {
231	// check the signature is valid under the expected authority and chain state.
232	let expected_author =
233		secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
234			.ok_or(Error::NoSecondaryAuthorExpected)?;
235
236	let author = &epoch
237		.authorities
238		.get(pre_digest.authority_index as usize)
239		.ok_or(Error::SlotAuthorNotFound)?
240		.0;
241
242	if expected_author != author {
243		return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()))
244	}
245
246	let mut epoch_index = epoch.epoch_index;
247	if epoch.end_slot() <= pre_digest.slot {
248		// Slot doesn't strictly belong to this epoch, create a clone with fixed values.
249		epoch_index = epoch.clone_for_slot(pre_digest.slot).epoch_index;
250	}
251
252	if !AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
253		return Err(Error::BadSignature(pre_hash))
254	}
255
256	let data = make_vrf_sign_data(&epoch.randomness, pre_digest.slot, epoch_index);
257
258	if !author.as_inner_ref().vrf_verify(&data, &pre_digest.vrf_signature) {
259		return Err(Error::VrfVerificationFailed)
260	}
261
262	Ok(())
263}