From e6009b068a7ba579010fdd2398df5058ef7e6b37 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 2 Nov 2021 11:45:00 +0100 Subject: [PATCH 1/2] Implement Babe blocks authorship --- src/author/babe.rs | 156 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/author/babe.rs diff --git a/src/author/babe.rs b/src/author/babe.rs new file mode 100644 index 0000000000..d2e8c0fffa --- /dev/null +++ b/src/author/babe.rs @@ -0,0 +1,156 @@ +// Smoldot +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::header; +use core::{num::NonZeroU64, time::Duration}; + +/// Configuration for [`next_slot_claim`]. +pub struct Config<'a, TLocAuth> { + /// Time elapsed since [the Unix Epoch](https://en.wikipedia.org/wiki/Unix_time) (i.e. + /// 00:00:00 UTC on 1 January 1970), ignoring leap seconds. + pub now_from_unix_epoch: Duration, + + /// Duration, in milliseconds, of a Babe slot. + pub slot_duration: NonZeroU64, + + /// List of the Babe authorities allowed to produce a block. This is either the same as the + /// ones of the current best block, or a new list if the current best block contains an + /// authorities list change digest item. + pub current_authorities: header::BabeAuthoritiesIter<'a>, + + /// Iterator to the list of sr25519 public keys available locally. + /// + /// Must implement `Iterator`. + pub local_authorities: TLocAuth, +} + +/// Calculates the earliest one of the authorities in [`Config::local_authorities`] is allowed to +/// produce a block. +/// +/// Returns `None` if none of the local authorities are allowed to produce blocks. +/// +/// The value returned by this function is entirely deterministic based on the [`Config`] and +/// never changes until [`Config::now_from_unix_epoch`] gets past the value returned in +/// [`SlotClaim::slot_end_from_unix_epoch`]. +/// +/// However, keep in mind that, as the best block changes, the list of authorities +/// ([`Config::current_authorities`]) might change, in which case this function should be +/// called again. +pub fn next_slot_claim<'a>( + config: Config<'a, impl Iterator>, +) -> Option { + let num_current_authorities = config.current_authorities.clone().count(); + + let current_slot = config.now_from_unix_epoch.as_secs() / config.slot_duration.get(); + + let current_slot_index = + usize::try_from(current_slot.checked_div(u64::try_from(num_current_authorities).unwrap())?) + .unwrap(); + + let mut claim = None; + + for (pub_key_index, local_pub_key) in config.local_authorities.enumerate() { + // TODO: O(n) complexity + let mut index = match config + .current_authorities + .clone() + .position(|pk| pk.public_key == local_pub_key) + { + Some(idx) => idx, + None => continue, + }; + + if index < current_slot_index { + index += num_current_authorities; + } + + let claimable_slot = current_slot + u64::try_from(index - current_slot_index).unwrap(); + + match claim { + Some((s, _)) if s <= claimable_slot => {} + _ => claim = Some((claimable_slot, pub_key_index)), + } + } + + if let Some((slot_number, local_authorities_index)) = claim { + let slot_start_from_unix_epoch = + Duration::from_secs(slot_number * config.slot_duration.get()); + let slot_end_from_unix_epoch = + slot_start_from_unix_epoch + Duration::from_secs(config.slot_duration.get()); + debug_assert!(slot_end_from_unix_epoch < config.now_from_unix_epoch); + + Some(SlotClaim { + slot_start_from_unix_epoch, + slot_end_from_unix_epoch, + slot_number, + local_authorities_index, + }) + } else { + None + } +} + +pub enum NextSlot { + Finished(Option), + SignVrf(SignVrf), +} + +/// Generating a VRF signature is necessary in order to continue. +pub struct SignVrf<'a> { + slot: u64, + epoch: u64, + randomness: Vec, +} + +impl<'a> SignVrf<'a> { + /// Returns the label of the transcript. + pub fn transcript_label(&self) -> &'static [u8] { + b"BABE" + } + + /// Returns the list of transcript items. + pub fn transcript_items( + &self, + ) -> impl Iterator, u64>)> { + [ + ("slot number", either::Right(self.slot)), + ("current epoch", either::Right(self.epoch)), + ("chain randomness", either::Left(&self.randomness)), + ] + } + + /// Resumes determining the next available Babe slot. + pub fn resume(self, vrf_signature: &[u8; 32]) {} +} + +/// Slot happening now or in the future and that can be attributed to one of the authorities in +/// [`Config::local_authorities`]. +/// +/// See also [`next_slot_claim`]. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SlotClaim { + /// UNIX time when the slot starts. Can be inferior to the value passed to + /// [`Config::now_from_unix_epoch`] if the slot has already started. + pub slot_start_from_unix_epoch: Duration, + /// UNIX time when the slot ends. Always superior to the value passed to + /// [`Config::now_from_unix_epoch`]. + pub slot_end_from_unix_epoch: Duration, + /// Slot number of the claim. Used when building the block. + pub slot_number: u64, + /// Index within [`Config::local_authorities`] of the authority that can produce the block. + pub local_authorities_index: usize, +} From 319d02f8a9ca399706a2388667e71559142fb7b5 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 2 Nov 2021 14:02:07 +0100 Subject: [PATCH 2/2] WIP --- src/author.rs | 1 + src/author/babe.rs | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/author.rs b/src/author.rs index 7131206a68..1976dc350c 100644 --- a/src/author.rs +++ b/src/author.rs @@ -18,5 +18,6 @@ // TODO: doc pub mod aura; +pub mod babe; pub mod build; pub mod runtime; diff --git a/src/author/babe.rs b/src/author/babe.rs index d2e8c0fffa..249b2d40f2 100644 --- a/src/author/babe.rs +++ b/src/author/babe.rs @@ -16,7 +16,7 @@ // along with this program. If not, see . use crate::header; -use core::{num::NonZeroU64, time::Duration}; +use core::{fmt, num::NonZeroU64, time::Duration}; /// Configuration for [`next_slot_claim`]. pub struct Config<'a, TLocAuth> { @@ -104,8 +104,12 @@ pub fn next_slot_claim<'a>( } } +/// Process of determining the next Babe authoring slot. +#[derive(Debug)] pub enum NextSlot { + /// Babe authoring slot has been determined. Finished(Option), + /// Generating a VRF signature is necessary in order to continue the process. SignVrf(SignVrf), } @@ -117,13 +121,13 @@ pub struct SignVrf<'a> { } impl<'a> SignVrf<'a> { - /// Returns the label of the transcript. - pub fn transcript_label(&self) -> &'static [u8] { + /// Returns the label of the transcript for the Vrf. + pub fn vrf_transcript_label(&self) -> &'static [u8] { b"BABE" } - /// Returns the list of transcript items. - pub fn transcript_items( + /// Returns the list of transcript items for the Vrf. + pub fn vrf_transcript_items( &self, ) -> impl Iterator, u64>)> { [ @@ -134,7 +138,15 @@ impl<'a> SignVrf<'a> { } /// Resumes determining the next available Babe slot. - pub fn resume(self, vrf_signature: &[u8; 32]) {} + pub fn resume(self, vrf_signature: &[u8; 32]) -> NextSlot { + todo!() + } +} + +impl<'a> fmt::Debug for SignVrf<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SignVrf").finish() + } } /// Slot happening now or in the future and that can be attributed to one of the authorities in