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
new file mode 100644
index 0000000000..249b2d40f2
--- /dev/null
+++ b/src/author/babe.rs
@@ -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 .
+
+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- `.
+ 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
+ }
+}
+
+/// 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),
+}
+
+/// 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 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
- , 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,
+}