BABE Randomness using PreRuntime digests (#2929)

* Initial work on exposing pre-runtime digests

This provides the primitive API, as well as exposing it from BABE.

* Initial work on using pre-digests in runtimes

This includes both code to expose them from `srml_system`, as well as
using it in (currently dead) code in `srml_babe`.

* Bump `{spec,impl}_version`

* Add `u64_backend` feature to curve25519-dalek

Otherwise, it errors out at compile-time.

* Bump `Cargo.lock`

* Do not depend on the schnorrkel crate in the runtime

The schnorrkel crate does not work on `#![no_std]`, but the runtime only
needs constants from it.  This adds our own definitions of those
constants, and checks them for correctness at compile-time.

* Actually implement storage of VRF outputs

* Trivial formatting change

* Provide a `hash_randomness` function in BABE

for processing VRF outputs.

* Implement a basic randomness generating function

It just XORs the VRF outputs together.

* Actually implement on-chain randomness

Blake2b is used for hashing.

* Update dependencies

* Run `cargo update` where needed

* Re-add a newline at EOF

* Remove broken and unsafe code

XOR is not a hash function, and must not be used as such.  The
implementation was also needlessly unsafe.

* Run `cargo update` where needed

* Remove spurious dependency

* Document security guarantees of BABE randomness

* Add a `RandomnessBeacon` trait

* Document `RandomnessBeacon::random`

* Fix silly compile error (unexpected type arguments)

* Fix BABE randomness

* Implement `FindAuthor` for `babe::Module`

* Apply suggestions from code review

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>
Co-Authored-By: Robert Habermeier <rphmeier@gmail.com>

* Respond to suggestions from code review and fix bugs

* Store an authority index, not the authority itself.
* Avoid unnecessary decoding.
* Implement relative slots and BABE randomness fully and correctly.

* Remove spurious dependency

* Fix error reported by rust-analyzer

* Update Cargo.lock files

* `wrapping_add` → `checked_add`

The epoch index will not overflow.  Panic if it does.

* Move randomness documentation to trait

* Fix compile error in test suite

* Explain 2^64 limit

Co-Authored-By: Robert Habermeier <rphmeier@gmail.com>
This commit is contained in:
DemiMarie-parity
2019-07-03 08:49:08 -04:00
committed by Gavin Wood
parent dcb1a590e2
commit 81d8a5d01d
18 changed files with 1810 additions and 1916 deletions
+12 -7
View File
@@ -48,10 +48,6 @@ pub trait SlotWorker<B: Block> {
/// triggered.
type OnSlot: IntoFuture<Item = (), Error = consensus_common::Error>;
/// Called when the proposer starts.
#[deprecated(note = "Not called. Please perform any initialization before calling start_slot_worker.")]
fn on_start(&self, _slot_duration: u64) -> Result<(), consensus_common::Error> { Ok(()) }
/// Called when a new slot is triggered.
fn on_slot(&self, chain_head: B::Header, slot_info: SlotInfo) -> Self::OnSlot;
}
@@ -60,8 +56,13 @@ pub trait SlotWorker<B: Block> {
pub trait SlotCompatible {
/// Extract timestamp and slot from inherent data.
fn extract_timestamp_and_slot(
&self,
inherent: &InherentData,
) -> Result<(u64, u64), consensus_common::Error>;
) -> Result<(u64, u64, std::time::Duration), consensus_common::Error>;
/// Get the difference between chain time and local time. Defaults to
/// always returning zero.
fn time_offset() -> SignedDuration { Default::default() }
}
/// Start a new slot worker.
@@ -74,6 +75,7 @@ pub fn start_slot_worker<B, C, W, T, SO, SC>(
worker: W,
sync_oracle: SO,
inherent_data_providers: InherentDataProviders,
timestamp_extractor: SC,
) -> impl Future<Item = (), Error = ()>
where
B: Block,
@@ -86,8 +88,11 @@ where
let SlotDuration(slot_duration) = slot_duration;
// rather than use a timer interval, we schedule our waits ourselves
let mut authorship = Slots::<SC>::new(slot_duration.slot_duration(), inherent_data_providers)
.map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e))
let mut authorship = Slots::<SC>::new(
slot_duration.slot_duration(),
inherent_data_providers,
timestamp_extractor,
).map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e))
.for_each(move |slot_info| {
// only propose when we are not syncing.
if sync_oracle.is_major_syncing() {
+47 -44
View File
@@ -24,7 +24,6 @@ use futures::prelude::*;
use futures::try_ready;
use inherents::{InherentData, InherentDataProviders};
use std::marker::PhantomData;
use std::time::{Duration, Instant};
use tokio_timer::Delay;
@@ -55,11 +54,11 @@ impl SignedDuration {
/// Get the slot for now. Panics if `slot_duration` is 0.
pub fn slot_now(&self, slot_duration: u64) -> u64 {
if self.is_positive {
(if self.is_positive {
duration_now() + self.offset
} else {
duration_now() - self.offset
}.as_secs() / slot_duration
}.as_secs()) / slot_duration
}
}
@@ -102,18 +101,22 @@ pub struct Slots<SC> {
slot_duration: u64,
inner_delay: Option<Delay>,
inherent_data_providers: InherentDataProviders,
_marker: PhantomData<SC>,
timestamp_extractor: SC,
}
impl<SC> Slots<SC> {
/// Create a new `Slots` stream.
pub fn new(slot_duration: u64, inherent_data_providers: InherentDataProviders) -> Self {
pub fn new(
slot_duration: u64,
inherent_data_providers: InherentDataProviders,
timestamp_extractor: SC,
) -> Self {
Slots {
last_slot: 0,
slot_duration,
inner_delay: None,
inherent_data_providers,
_marker: PhantomData,
timestamp_extractor,
}
}
}
@@ -123,49 +126,49 @@ impl<SC: SlotCompatible> Stream for Slots<SC> {
type Error = Error;
fn poll(&mut self) -> Poll<Option<SlotInfo>, Self::Error> {
let slot_duration = self.slot_duration;
self.inner_delay = match self.inner_delay.take() {
None => {
// schedule wait.
let wait_until = Instant::now() + time_until_next(duration_now(), slot_duration);
Some(Delay::new(wait_until))
loop {
let slot_duration = self.slot_duration;
self.inner_delay = match self.inner_delay.take() {
None => {
// schedule wait.
let wait_until = Instant::now() + time_until_next(duration_now(), slot_duration);
Some(Delay::new(wait_until))
}
Some(d) => Some(d),
};
if let Some(ref mut inner_delay) = self.inner_delay {
try_ready!(inner_delay
.poll()
.map_err(Error::FaultyTimer));
}
Some(d) => Some(d),
};
if let Some(ref mut inner_delay) = self.inner_delay {
try_ready!(inner_delay
.poll()
.map_err(Error::FaultyTimer));
}
// timeout has fired.
// timeout has fired.
let inherent_data = self
.inherent_data_providers
.create_inherent_data()
.map_err(|s| consensus_common::Error::InherentData(s.into_owned()))?;
let (timestamp, slot_num, offset) = self
.timestamp_extractor
.extract_timestamp_and_slot(&inherent_data)?;
// reschedule delay for next slot.
let ends_at = Instant::now() + offset +
time_until_next(Duration::from_secs(timestamp), slot_duration);
self.inner_delay = Some(Delay::new(ends_at));
let inherent_data = self
.inherent_data_providers
.create_inherent_data()
.map_err(|s| consensus_common::Error::InherentData(s.into_owned()))?;
let (timestamp, slot_num) = SC::extract_timestamp_and_slot(&inherent_data)?;
// never yield the same slot twice.
if slot_num > self.last_slot {
self.last_slot = slot_num;
// reschedule delay for next slot.
let ends_at =
Instant::now() + time_until_next(Duration::from_secs(timestamp), slot_duration);
self.inner_delay = Some(Delay::new(ends_at));
// never yield the same slot twice.
if slot_num > self.last_slot {
self.last_slot = slot_num;
Ok(Async::Ready(Some(SlotInfo {
number: slot_num,
duration: self.slot_duration,
timestamp,
ends_at,
inherent_data,
})))
} else {
// re-poll until we get a new slot.
self.poll()
break Ok(Async::Ready(Some(SlotInfo {
number: slot_num,
duration: self.slot_duration,
timestamp,
ends_at,
inherent_data,
})))
}
}
}
}