Skip to content
Draft
Changes from 1 commit
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
156 changes: 156 additions & 0 deletions src/author/babe.rs
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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<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
}
}

pub enum NextSlot {
Finished(Option<SlotClaim>),
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.
pub fn transcript_label(&self) -> &'static [u8] {
b"BABE"
}

/// Returns the list of transcript items.
pub fn 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]) {}
}

/// 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,
}