Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
// TODO: doc

pub mod aura;
pub mod babe;
pub mod build;
pub mod runtime;
168 changes: 168 additions & 0 deletions src/author/babe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// 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 <http://www.gnu.org/licenses/>.

use crate::header;
use core::{fmt, 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<Item = &[u8; 32]>`.
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<Item = &'a [u8; 32]>>,
) -> Option<SlotClaim> {
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
}
}

/// Process of determining the next Babe authoring slot.
#[derive(Debug)]
pub enum NextSlot {
/// Babe authoring slot has been determined.
Finished(Option<SlotClaim>),
/// Generating a VRF signature is necessary in order to continue the process.
SignVrf(SignVrf),
}

/// Generating a VRF signature is necessary in order to continue.
pub struct SignVrf<'a> {
slot: u64,
epoch: u64,
randomness: Vec<u8>,
}

impl<'a> SignVrf<'a> {
/// 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 for the Vrf.
pub fn vrf_transcript_items(
&self,
) -> impl Iterator<Item = (&'static [u8], either::Either<impl AsRef<[u8]>, 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]) -> 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
/// [`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,
}