subsoil/consensus/babe/mod.rs
1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Primitives for BABE.
8#![deny(warnings)]
9#![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)]
10
11pub mod digests;
12pub mod inherents;
13
14use crate::runtime::{traits::Header, ConsensusEngineId};
15#[cfg(not(feature = "std"))]
16use alloc::vec::Vec;
17use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
18use scale_info::TypeInfo;
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22use digests::{NextConfigDescriptor, NextEpochDescriptor};
23
24pub use crate::core::sr25519::vrf::{
25 VrfInput, VrfPreOutput, VrfProof, VrfSignData, VrfSignature, VrfTranscript,
26};
27
28/// Key type for BABE module.
29pub const KEY_TYPE: crate::core::crypto::KeyTypeId = crate::application_crypto::key_types::BABE;
30
31mod app {
32 use crate::application_crypto::{key_types::BABE, sr25519};
33 crate::app_crypto!(sr25519, BABE);
34}
35
36/// VRF context used for per-slot randomness generation.
37pub const RANDOMNESS_VRF_CONTEXT: &[u8] = b"BabeVRFInOutContext";
38
39/// VRF output length for per-slot randomness.
40pub const RANDOMNESS_LENGTH: usize = 32;
41
42/// Randomness type required by BABE operations.
43pub type Randomness = [u8; RANDOMNESS_LENGTH];
44
45/// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in
46/// the main Babe module. If that ever changes, then this must, too.
47#[cfg(feature = "std")]
48pub type AuthorityPair = app::Pair;
49
50/// A Babe authority signature.
51pub type AuthoritySignature = app::Signature;
52
53/// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in
54/// the main Babe module. If that ever changes, then this must, too.
55pub type AuthorityId = app::Public;
56
57/// The `ConsensusEngineId` of BABE.
58pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE";
59
60/// The length of the public key
61pub const PUBLIC_KEY_LENGTH: usize = 32;
62
63/// How many blocks to wait before running the median algorithm for relative time
64/// This will not vary from chain to chain as it is not dependent on slot duration
65/// or epoch length.
66pub const MEDIAN_ALGORITHM_CARDINALITY: usize = 1200; // arbitrary suggestion by w3f-research.
67
68/// The index of an authority.
69pub type AuthorityIndex = u32;
70
71pub use crate::consensus::slots::{Slot, SlotDuration};
72
73/// An equivocation proof for multiple block authorships on the same slot (i.e. double vote).
74pub type EquivocationProof<H> = crate::consensus::slots::EquivocationProof<H, AuthorityId>;
75
76/// The weight of an authority.
77// NOTE: we use a unique name for the weight to avoid conflicts with other
78// `Weight` types, since the metadata isn't able to disambiguate.
79pub type BabeAuthorityWeight = u64;
80
81/// The cumulative weight of a BABE block, i.e. sum of block weights starting
82/// at this block until the genesis block.
83///
84/// Primary blocks have a weight of 1 whereas secondary blocks have a weight
85/// of 0 (regardless of whether they are plain or vrf secondary blocks).
86pub type BabeBlockWeight = u32;
87
88/// Make VRF input suitable for BABE's randomness generation.
89pub fn make_vrf_transcript(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfInput {
90 VrfInput::new(
91 &BABE_ENGINE_ID,
92 &[
93 (b"slot number", &slot.to_le_bytes()),
94 (b"current epoch", &epoch.to_le_bytes()),
95 (b"chain randomness", randomness),
96 ],
97 )
98}
99
100/// Make VRF signing data suitable for BABE's protocol.
101pub fn make_vrf_sign_data(randomness: &Randomness, slot: Slot, epoch: u64) -> VrfSignData {
102 make_vrf_transcript(randomness, slot, epoch).into()
103}
104
105/// An consensus log item for BABE.
106#[derive(Decode, Encode, Clone, PartialEq, Eq)]
107pub enum ConsensusLog {
108 /// The epoch has changed. This provides information about the _next_
109 /// epoch - information about the _current_ epoch (i.e. the one we've just
110 /// entered) should already be available earlier in the chain.
111 #[codec(index = 1)]
112 NextEpochData(NextEpochDescriptor),
113 /// Disable the authority with given index.
114 #[codec(index = 2)]
115 OnDisabled(AuthorityIndex),
116 /// The epoch has changed, and the epoch after the current one will
117 /// enact different epoch configurations.
118 #[codec(index = 3)]
119 NextConfigData(NextConfigDescriptor),
120}
121
122/// Configuration data used by the BABE consensus engine.
123#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
124pub struct BabeConfigurationV1 {
125 /// The slot duration in milliseconds for BABE. Currently, only
126 /// the value provided by this type at genesis will be used.
127 ///
128 /// Dynamic slot duration may be supported in the future.
129 pub slot_duration: u64,
130
131 /// The duration of epochs in slots.
132 pub epoch_length: u64,
133
134 /// A constant value that is used in the threshold calculation formula.
135 /// Expressed as a rational where the first member of the tuple is the
136 /// numerator and the second is the denominator. The rational should
137 /// represent a value between 0 and 1.
138 /// In the threshold formula calculation, `1 - c` represents the probability
139 /// of a slot being empty.
140 pub c: (u64, u64),
141
142 /// The authorities for the genesis epoch.
143 pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
144
145 /// The randomness for the genesis epoch.
146 pub randomness: Randomness,
147
148 /// Whether this chain should run with secondary slots, which are assigned
149 /// in round-robin manner.
150 pub secondary_slots: bool,
151}
152
153impl From<BabeConfigurationV1> for BabeConfiguration {
154 fn from(v1: BabeConfigurationV1) -> Self {
155 Self {
156 slot_duration: v1.slot_duration,
157 epoch_length: v1.epoch_length,
158 c: v1.c,
159 authorities: v1.authorities,
160 randomness: v1.randomness,
161 allowed_slots: if v1.secondary_slots {
162 AllowedSlots::PrimaryAndSecondaryPlainSlots
163 } else {
164 AllowedSlots::PrimarySlots
165 },
166 }
167 }
168}
169
170/// Configuration data used by the BABE consensus engine.
171#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, TypeInfo)]
172pub struct BabeConfiguration {
173 /// The slot duration in milliseconds for BABE. Currently, only
174 /// the value provided by this type at genesis will be used.
175 ///
176 /// Dynamic slot duration may be supported in the future.
177 pub slot_duration: u64,
178
179 /// The duration of epochs in slots.
180 pub epoch_length: u64,
181
182 /// A constant value that is used in the threshold calculation formula.
183 /// Expressed as a rational where the first member of the tuple is the
184 /// numerator and the second is the denominator. The rational should
185 /// represent a value between 0 and 1.
186 /// In the threshold formula calculation, `1 - c` represents the probability
187 /// of a slot being empty.
188 pub c: (u64, u64),
189
190 /// The authorities
191 pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
192
193 /// The randomness
194 pub randomness: Randomness,
195
196 /// Type of allowed slots.
197 pub allowed_slots: AllowedSlots,
198}
199
200impl BabeConfiguration {
201 /// Convenience method to get the slot duration as a `SlotDuration` value.
202 pub fn slot_duration(&self) -> SlotDuration {
203 SlotDuration::from_millis(self.slot_duration)
204 }
205}
206
207/// Types of allowed slots.
208#[derive(
209 Clone,
210 Copy,
211 PartialEq,
212 Eq,
213 Encode,
214 Decode,
215 DecodeWithMemTracking,
216 Debug,
217 MaxEncodedLen,
218 TypeInfo,
219)]
220#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
221pub enum AllowedSlots {
222 /// Only allow primary slots.
223 PrimarySlots,
224 /// Allow primary and secondary plain slots.
225 PrimaryAndSecondaryPlainSlots,
226 /// Allow primary and secondary VRF slots.
227 PrimaryAndSecondaryVRFSlots,
228}
229
230impl AllowedSlots {
231 /// Whether plain secondary slots are allowed.
232 pub fn is_secondary_plain_slots_allowed(&self) -> bool {
233 *self == Self::PrimaryAndSecondaryPlainSlots
234 }
235
236 /// Whether VRF secondary slots are allowed.
237 pub fn is_secondary_vrf_slots_allowed(&self) -> bool {
238 *self == Self::PrimaryAndSecondaryVRFSlots
239 }
240}
241
242/// Configuration data used by the BABE consensus engine that may change with epochs.
243#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug, MaxEncodedLen, TypeInfo)]
244#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245pub struct BabeEpochConfiguration {
246 /// A constant value that is used in the threshold calculation formula.
247 /// Expressed as a rational where the first member of the tuple is the
248 /// numerator and the second is the denominator. The rational should
249 /// represent a value between 0 and 1.
250 /// In the threshold formula calculation, `1 - c` represents the probability
251 /// of a slot being empty.
252 pub c: (u64, u64),
253
254 /// Whether this chain should run with secondary slots, which are assigned
255 /// in round-robin manner.
256 pub allowed_slots: AllowedSlots,
257}
258
259impl Default for BabeEpochConfiguration {
260 fn default() -> Self {
261 Self { c: (1, 4), allowed_slots: AllowedSlots::PrimaryAndSecondaryVRFSlots }
262 }
263}
264
265/// Verifies the equivocation proof by making sure that: both headers have
266/// different hashes, are targeting the same slot, and have valid signatures by
267/// the same authority.
268pub fn check_equivocation_proof<H>(proof: EquivocationProof<H>) -> bool
269where
270 H: Header,
271{
272 use crate::application_crypto::RuntimeAppPublic;
273 use digests::*;
274
275 let find_pre_digest =
276 |header: &H| header.digest().logs().iter().find_map(|log| log.as_babe_pre_digest());
277
278 let verify_seal_signature = |mut header: H, offender: &AuthorityId| {
279 let seal = header.digest_mut().pop()?.as_babe_seal()?;
280 let pre_hash = header.hash();
281
282 if !offender.verify(&pre_hash.as_ref(), &seal) {
283 return None;
284 }
285
286 Some(())
287 };
288
289 let verify_proof = || {
290 // we must have different headers for the equivocation to be valid
291 if proof.first_header.hash() == proof.second_header.hash() {
292 return None;
293 }
294
295 let first_pre_digest = find_pre_digest(&proof.first_header)?;
296 let second_pre_digest = find_pre_digest(&proof.second_header)?;
297
298 // both headers must be targeting the same slot and it must
299 // be the same as the one in the proof.
300 if proof.slot != first_pre_digest.slot()
301 || first_pre_digest.slot() != second_pre_digest.slot()
302 {
303 return None;
304 }
305
306 // both headers must have been authored by the same authority
307 if first_pre_digest.authority_index() != second_pre_digest.authority_index() {
308 return None;
309 }
310
311 // we finally verify that the expected authority has signed both headers and
312 // that the signature is valid.
313 verify_seal_signature(proof.first_header, &proof.offender)?;
314 verify_seal_signature(proof.second_header, &proof.offender)?;
315
316 Some(())
317 };
318
319 // NOTE: we isolate the verification code into an helper function that
320 // returns `Option<()>` so that we can use `?` to deal with any intermediate
321 // errors and discard the proof as invalid.
322 verify_proof().is_some()
323}
324
325/// An opaque type used to represent the key ownership proof at the runtime API
326/// boundary. The inner value is an encoded representation of the actual key
327/// ownership proof which will be parameterized when defining the runtime. At
328/// the runtime API boundary this type is unknown and as such we keep this
329/// opaque representation, implementors of the runtime API will have to make
330/// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type.
331#[derive(Decode, Encode, PartialEq, TypeInfo)]
332pub struct OpaqueKeyOwnershipProof(Vec<u8>);
333impl OpaqueKeyOwnershipProof {
334 /// Create a new `OpaqueKeyOwnershipProof` using the given encoded
335 /// representation.
336 pub fn new(inner: Vec<u8>) -> OpaqueKeyOwnershipProof {
337 OpaqueKeyOwnershipProof(inner)
338 }
339
340 /// Try to decode this `OpaqueKeyOwnershipProof` into the given concrete key
341 /// ownership proof type.
342 pub fn decode<T: Decode>(self) -> Option<T> {
343 Decode::decode(&mut &self.0[..]).ok()
344 }
345}
346
347/// BABE epoch information
348#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)]
349pub struct Epoch {
350 /// The epoch index.
351 pub epoch_index: u64,
352 /// The starting slot of the epoch.
353 pub start_slot: Slot,
354 /// The duration of this epoch.
355 pub duration: u64,
356 /// The authorities and their weights.
357 pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
358 /// Randomness for this epoch.
359 pub randomness: Randomness,
360 /// Configuration of the epoch.
361 pub config: BabeEpochConfiguration,
362}
363
364/// Returns the epoch index the given slot belongs to.
365pub fn epoch_index(slot: Slot, genesis_slot: Slot, epoch_duration: u64) -> u64 {
366 *slot.saturating_sub(genesis_slot) / epoch_duration
367}
368
369/// Returns the first slot at the given epoch index.
370pub fn epoch_start_slot(epoch_index: u64, genesis_slot: Slot, epoch_duration: u64) -> Slot {
371 // (epoch_index * epoch_duration) + genesis_slot
372
373 const PROOF: &str = "slot number is u64; it should relate in some way to wall clock time; \
374 if u64 is not enough we should crash for safety; qed.";
375
376 epoch_index
377 .checked_mul(epoch_duration)
378 .and_then(|slot| slot.checked_add(*genesis_slot))
379 .expect(PROOF)
380 .into()
381}
382
383crate::api::decl_runtime_apis! {
384 /// API necessary for block authorship with BABE.
385 #[api_version(2)]
386 pub trait BabeApi {
387 /// Return the configuration for BABE.
388 fn configuration() -> BabeConfiguration;
389
390 /// Return the configuration for BABE. Version 1.
391 #[changed_in(2)]
392 fn configuration() -> BabeConfigurationV1;
393
394 /// Returns the slot that started the current epoch.
395 fn current_epoch_start() -> Slot;
396
397 /// Returns information regarding the current epoch.
398 fn current_epoch() -> Epoch;
399
400 /// Returns information regarding the next epoch (which was already
401 /// previously announced).
402 fn next_epoch() -> Epoch;
403
404 /// Generates a proof of key ownership for the given authority in the
405 /// current epoch. An example usage of this module is coupled with the
406 /// session historical module to prove that a given authority key is
407 /// tied to a given staking identity during a specific session. Proofs
408 /// of key ownership are necessary for submitting equivocation reports.
409 /// NOTE: even though the API takes a `slot` as parameter the current
410 /// implementations ignores this parameter and instead relies on this
411 /// method being called at the correct block height, i.e. any point at
412 /// which the epoch for the given slot is live on-chain. Future
413 /// implementations will instead use indexed data through an offchain
414 /// worker, not requiring older states to be available.
415 fn generate_key_ownership_proof(
416 slot: Slot,
417 authority_id: AuthorityId,
418 ) -> Option<OpaqueKeyOwnershipProof>;
419
420 /// Submits an unsigned extrinsic to report an equivocation. The caller
421 /// must provide the equivocation proof and a key ownership proof
422 /// (should be obtained using `generate_key_ownership_proof`). The
423 /// extrinsic will be unsigned and should only be accepted for local
424 /// authorship (not to be broadcast to the network). This method returns
425 /// `None` when creation of the extrinsic fails, e.g. if equivocation
426 /// reporting is disabled for the given runtime (i.e. this method is
427 /// hardcoded to return `None`). Only useful in an offchain context.
428 fn submit_report_equivocation_unsigned_extrinsic(
429 equivocation_proof: EquivocationProof<Block::Header>,
430 key_owner_proof: OpaqueKeyOwnershipProof,
431 ) -> Option<()>;
432 }
433}