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
+450 -399
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -117,12 +117,14 @@ struct AuraSlotCompatible;
impl SlotCompatible for AuraSlotCompatible {
fn extract_timestamp_and_slot(
&self,
data: &InherentData
) -> Result<(TimestampInherent, AuraInherent), consensus_common::Error> {
) -> Result<(TimestampInherent, AuraInherent, std::time::Duration), consensus_common::Error> {
data.timestamp_inherent_data()
.and_then(|t| data.aura_inherent_data().map(|a| (t, a)))
.map_err(Into::into)
.map_err(consensus_common::Error::InherentData)
.map(|(x, y)| (x, y, Default::default()))
}
}
@@ -170,7 +172,8 @@ pub fn start_aura<B, C, SC, E, I, P, SO, Error, H>(
select_chain,
worker,
sync_oracle,
inherent_data_providers
inherent_data_providers,
AuraSlotCompatible,
))
}
@@ -514,7 +517,7 @@ impl<B: Block, C, P> Verifier<B> for AuraVerifier<C, P> where
mut body: Option<Vec<B::Extrinsic>>,
) -> Result<(ImportBlock<B>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
let mut inherent_data = self.inherent_data_providers.create_inherent_data().map_err(String::from)?;
let (timestamp_now, slot_now) = AuraSlotCompatible::extract_timestamp_and_slot(&inherent_data)
let (timestamp_now, slot_now, _) = AuraSlotCompatible.extract_timestamp_and_slot(&inherent_data)
.map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?;
let hash = header.hash();
let parent_hash = *header.parent_hash();
@@ -31,6 +31,15 @@ pub type AuthorityId = Public;
/// The `ConsensusEngineId` of BABE.
pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE";
/// The length of the VRF output
pub const VRF_OUTPUT_LENGTH: usize = 32;
/// The length of the VRF proof
pub const VRF_PROOF_LENGTH: usize = 64;
/// The length of the public key
pub const PUBLIC_KEY_LENGTH: usize = 32;
/// Configuration data used by the BABE consensus engine.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)]
pub struct BabeConfiguration {
+14 -13
View File
@@ -16,15 +16,12 @@
//! Private implementation details of BABE digests.
use primitives::sr25519::{Public, Signature};
use babe_primitives::BABE_ENGINE_ID;
use primitives::sr25519::Signature;
use babe_primitives::{self, BABE_ENGINE_ID};
use runtime_primitives::{DigestItem, generic::OpaqueDigestItemId};
use std::fmt::Debug;
use parity_codec::{Decode, Encode, Codec, Input};
use schnorrkel::{
vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH},
PUBLIC_KEY_LENGTH,
};
use schnorrkel::{vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}};
/// A BABE pre-digest. It includes:
///
@@ -36,26 +33,26 @@ use schnorrkel::{
pub struct BabePreDigest {
pub(super) vrf_output: VRFOutput,
pub(super) proof: VRFProof,
pub(super) author: Public,
pub(super) index: u64,
pub(super) slot_num: u64,
}
/// The prefix used by BABE for its VRF keys.
pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf";
type TmpDecode = (
type RawBabePreDigest = (
[u8; VRF_OUTPUT_LENGTH],
[u8; VRF_PROOF_LENGTH],
[u8; PUBLIC_KEY_LENGTH],
u64,
u64,
);
impl Encode for BabePreDigest {
fn encode(&self) -> Vec<u8> {
let tmp: TmpDecode = (
let tmp: RawBabePreDigest = (
*self.vrf_output.as_bytes(),
self.proof.to_bytes(),
self.author.0,
self.index,
self.slot_num,
);
parity_codec::Encode::encode(&tmp)
@@ -64,11 +61,15 @@ impl Encode for BabePreDigest {
impl Decode for BabePreDigest {
fn decode<R: Input>(i: &mut R) -> Option<Self> {
let (output, proof, public_key, slot_num): TmpDecode = Decode::decode(i)?;
let (output, proof, index, slot_num): RawBabePreDigest = Decode::decode(i)?;
// Verify (at compile time) that the sizes in babe_primitives are correct
let _: [u8; babe_primitives::VRF_OUTPUT_LENGTH] = output;
let _: [u8; babe_primitives::VRF_PROOF_LENGTH] = proof;
Some(BabePreDigest {
proof: VRFProof::from_bytes(&proof).ok()?,
vrf_output: VRFOutput::from_bytes(&output).ok()?,
author: Public(public_key),
index,
slot_num,
})
}
+57 -46
View File
@@ -23,7 +23,8 @@
//! This crate is highly unstable and experimental. Breaking changes may
//! happen at any point. This crate is also missing features, such as banning
//! of malicious validators, that are essential for a production network.
#![forbid(unsafe_code, missing_docs)]
#![forbid(unsafe_code, missing_docs, unused_must_use)]
#![cfg_attr(not(test), forbid(dead_code))]
extern crate core;
mod digest;
use digest::CompatibleDigestItem;
@@ -120,17 +121,17 @@ impl Config {
}
}
struct BabeSlotCompatible;
impl SlotCompatible for BabeSlotCompatible {
impl SlotCompatible for BabeLink {
fn extract_timestamp_and_slot(
data: &InherentData
) -> Result<(TimestampInherent, u64), consensus_common::Error> {
&self,
data: &InherentData,
) -> Result<(TimestampInherent, u64, std::time::Duration), consensus_common::Error> {
trace!(target: "babe", "extract timestamp");
data.timestamp_inherent_data()
.and_then(|t| data.babe_inherent_data().map(|a| (t, a)))
.map_err(Into::into)
.map_err(consensus_common::Error::InherentData)
.map(|(x, y)| (x, y, self.0.lock().0.take().unwrap_or_default()))
}
}
@@ -164,6 +165,9 @@ pub struct BabeParams<C, E, I, SO, SC> {
/// Force authoring of blocks even if we are offline
pub force_authoring: bool,
/// The source of timestamps for relative slots
pub time_source: BabeLink,
}
/// Start the babe worker. The returned future should be run in a tokio runtime.
@@ -177,6 +181,7 @@ pub fn start_babe<B, C, SC, E, I, SO, Error, H>(BabeParams {
sync_oracle,
inherent_data_providers,
force_authoring,
time_source,
}: BabeParams<C, E, I, SO, SC>) -> Result<
impl Future<Item=(), Error=()>,
consensus_common::Error,
@@ -203,12 +208,13 @@ pub fn start_babe<B, C, SC, E, I, SO, Error, H>(BabeParams {
threshold: config.threshold(),
};
register_babe_inherent_data_provider(&inherent_data_providers, config.0.slot_duration())?;
Ok(slots::start_slot_worker::<_, _, _, _, _, BabeSlotCompatible>(
Ok(slots::start_slot_worker::<_, _, _, _, _, _>(
config.0,
select_chain,
worker,
sync_oracle,
inherent_data_providers
inherent_data_providers,
time_source,
))
}
@@ -279,7 +285,7 @@ impl<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> w
// FIXME replace the dummy empty slices with real data
// https://github.com/paritytech/substrate/issues/2435
// https://github.com/paritytech/substrate/issues/2436
let proposal_work = if let Some((inout, proof, _batchable_proof)) = claim_slot(
let proposal_work = if let Some(((inout, proof, _batchable_proof), index)) = claim_slot(
&[0u8; 0],
slot_info.number,
&[0u8; 0],
@@ -312,7 +318,7 @@ impl<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> w
let inherent_digest = BabePreDigest {
proof,
vrf_output: inout.to_output(),
author: pair.public(),
index: index as u64,
slot_num,
};
@@ -459,17 +465,17 @@ fn check_header<B: Block + Sized, C: AuxStore>(
})?;
let pre_digest = find_pre_digest::<B>(&header)?;
let BabePreDigest { slot_num, ref author, ref proof, ref vrf_output } = pre_digest;
let BabePreDigest { slot_num, index, ref proof, ref vrf_output } = pre_digest;
if slot_num > slot_now {
header.digest_mut().push(seal);
Ok(CheckedHeader::Deferred(header, slot_num))
} else if !authorities.contains(&author) {
} else if index > authorities.len() as u64 {
Err(babe_err!("Slot author not found"))
} else {
let pre_hash = header.hash();
let (pre_hash, author): (_, &sr25519::Public) = (header.hash(), &authorities[index as usize]);
if sr25519::Pair::verify(&sig, pre_hash, author) {
if sr25519::Pair::verify(&sig, pre_hash, author.clone()) {
let (inout, _batchable_proof) = {
let transcript = make_transcript(
Default::default(),
@@ -513,12 +519,16 @@ fn check_header<B: Block + Sized, C: AuxStore>(
}
}
/// State that must be shared between the import queue and the authoring logic.
#[derive(Default, Clone, Debug)]
pub struct BabeLink(Arc<Mutex<(Option<Duration>, Vec<(Instant, u64)>)>>);
/// A verifier for Babe blocks.
pub struct BabeVerifier<C> {
client: Arc<C>,
inherent_data_providers: inherents::InherentDataProviders,
config: Config,
timestamps: Mutex<(Option<Duration>, Vec<(Instant, u64)>)>,
time_source: BabeLink,
}
impl<C> BabeVerifier<C> {
@@ -551,11 +561,11 @@ fn median_algorithm(
slot_duration: u64,
slot_num: u64,
slot_now: u64,
timestamps: &mut (Option<Duration>, Vec<(Instant, u64)>),
time_source: &mut (Option<Duration>, Vec<(Instant, u64)>),
) {
let num_timestamps = timestamps.1.len();
let num_timestamps = time_source.1.len();
if num_timestamps as u64 >= median_required_blocks && median_required_blocks > 0 {
let mut new_list: Vec<_> = timestamps.1.iter().map(|&(t, sl)| {
let mut new_list: Vec<_> = time_source.1.iter().map(|&(t, sl)| {
let offset: u128 = u128::from(slot_duration)
.checked_mul(1_000_000u128) // self.config.get() returns *milliseconds*
.and_then(|x| x.checked_mul(u128::from(slot_num).saturating_sub(u128::from(sl))))
@@ -570,11 +580,11 @@ fn median_algorithm(
let &median = new_list
.get(num_timestamps / 2)
.expect("we have at least one timestamp, so this is a valid index; qed");
timestamps.1.clear();
time_source.1.clear();
// FIXME #2927: pass this to the block authoring logic somehow
timestamps.0.replace(Instant::now() - median);
time_source.0.replace(Instant::now() - median);
} else {
timestamps.1.push((Instant::now(), slot_now))
time_source.1.push((Instant::now(), slot_now))
}
}
@@ -604,7 +614,7 @@ impl<B: Block, C> Verifier<B> for BabeVerifier<C> where
.inherent_data_providers
.create_inherent_data()
.map_err(String::from)?;
let (_, slot_now) = BabeSlotCompatible::extract_timestamp_and_slot(&inherent_data)
let (_, slot_now, _) = self.time_source.extract_timestamp_and_slot(&inherent_data)
.map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?;
let hash = header.hash();
let parent_hash = *header.parent_hash();
@@ -672,7 +682,7 @@ impl<B: Block, C> Verifier<B> for BabeVerifier<C> where
self.config.get(),
slot_num,
slot_now,
&mut *self.timestamps.lock(),
&mut *self.time_source.0.lock(),
);
// FIXME #1019 extract authorities
Ok((import_block, maybe_keys))
@@ -765,8 +775,9 @@ fn claim_slot(
authorities: &[AuthorityId],
key: &sr25519::Pair,
threshold: u64,
) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> {
if !authorities.contains(&key.public()) { return None }
) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize)> {
let public = &key.public();
let index = authorities.iter().position(|s| s == public)?;
let transcript = make_transcript(
randomness,
slot_number,
@@ -780,7 +791,9 @@ fn claim_slot(
// be empty. Therefore, this division is safe.
let threshold = threshold / authorities.len() as u64;
get_keypair(key).vrf_sign_n_check(transcript, |inout| check(inout, threshold))
get_keypair(key)
.vrf_sign_n_check(transcript, |inout| check(inout, threshold))
.map(|s|(s, index))
}
fn initialize_authorities_cache<B, C>(client: &C) -> Result<(), ConsensusError> where
@@ -822,7 +835,7 @@ pub fn import_queue<B, C, E>(
finality_proof_request_builder: Option<SharedFinalityProofRequestBuilder<B>>,
client: Arc<C>,
inherent_data_providers: InherentDataProviders,
) -> Result<BabeImportQueue<B>, consensus_common::Error> where
) -> Result<(BabeImportQueue<B>, BabeLink), consensus_common::Error> where
B: Block,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B> + BabeApi<B>,
@@ -832,28 +845,27 @@ pub fn import_queue<B, C, E>(
register_babe_inherent_data_provider(&inherent_data_providers, config.get())?;
initialize_authorities_cache(&*client)?;
let verifier = Arc::new(
BabeVerifier {
client: client,
inherent_data_providers,
timestamps: Default::default(),
config,
}
);
Ok(BasicQueue::new(
verifier,
let verifier = BabeVerifier {
client: client,
inherent_data_providers,
time_source: Default::default(),
config,
};
let timestamp_core = verifier.time_source.clone();
Ok((BasicQueue::new(
Arc::new(verifier),
block_import,
justification_import,
finality_proof_import,
finality_proof_request_builder,
))
), timestamp_core))
}
#[cfg(test)]
#[allow(dead_code, unused_imports, deprecated)]
// FIXME #2532: need to allow deprecated until refactor is done
// https://github.com/paritytech/substrate/issues/2532
#[cfg(test)]
#[allow(unused_imports, deprecated)]
#[cfg_attr(test, allow(dead_code))]
mod tests {
use super::*;
@@ -947,7 +959,7 @@ mod tests {
client,
inherent_data_providers,
config,
timestamps: Default::default(),
time_source: Default::default(),
})
}
@@ -1028,7 +1040,7 @@ mod tests {
#[allow(deprecated)]
let select_chain = LongestChain::new(client.backend().clone());
let babe = start_babe(BabeParams {
runtime.spawn(start_babe(BabeParams {
config,
local_key: Arc::new(key.clone().into()),
block_import: client.clone(),
@@ -1038,9 +1050,8 @@ mod tests {
sync_oracle: DummyOracle,
inherent_data_providers,
force_authoring: false,
}).expect("Starts babe");
runtime.spawn(babe);
time_source: Default::default(),
}).expect("Starts babe"));
}
debug!(target: "babe", "checkpoint 5");
+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,
})))
}
}
}
}
+17 -17
View File
@@ -15,7 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -33,12 +33,12 @@ dependencies = [
[[package]]
name = "hash-db"
version = "0.12.2"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hash256-std-hasher"
version = "0.12.2"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -82,7 +82,7 @@ dependencies = [
"proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -157,14 +157,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.91"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sr-io"
version = "2.0.0"
dependencies = [
"hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 2.0.0",
@@ -197,9 +197,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "substrate-primitives"
version = "2.0.0"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hash256-std-hasher 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -209,7 +209,7 @@ dependencies = [
[[package]]
name = "syn"
version = "0.15.34"
version = "0.15.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -222,7 +222,7 @@ name = "toml"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -230,7 +230,7 @@ name = "uint"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -243,11 +243,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
"checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e"
"checksum hash-db 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba7fb417e5c470acdd61068c79767d0e65962e70836cf6c9dfd2409f06345ce0"
"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86"
"checksum hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c95a428c86ed4633d83e07ef9e0a147a906da01e931f07e74a85bedce5a43"
"checksum hash256-std-hasher 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "663ce20dae36902c16d12c6aaae400ca40d922407a8cf2b4caf8cae9b39b4f03"
"checksum impl-codec 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62ed8ff267bc916dd848a800b96d3129aec73d5b23a5e3d018c83655d0c55371"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
@@ -261,9 +261,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd"
"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b"
"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe"
"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c"
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
"checksum uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5375d2c574f89adad4108ad525c93e39669853a602560bf5ed4ca9943b10799"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+1 -1
View File
@@ -19,7 +19,7 @@ hash256-std-hasher = { version = "0.12", default-features = false }
ed25519-dalek = { version = "1.0.0-pre.1", optional = true }
base58 = { version = "0.1", optional = true }
blake2-rfc = { version = "0.2.18", optional = true }
schnorrkel = { version = "0.1", optional = true }
schnorrkel = { version = "0.1.1", optional = true }
rand = { version = "0.6", optional = true }
sha2 = { version = "0.8", optional = true }
substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39", optional = true }
@@ -594,6 +594,21 @@ pub trait MaybeHash {}
#[cfg(not(feature = "std"))]
impl<T> MaybeHash for T {}
/// A type that provides a randomness beacon.
pub trait RandomnessBeacon {
/// Returns 32 bytes of random data. The output will change eventually, but
/// is not guaranteed to be different between any two calls.
///
/// # Security
///
/// This MUST NOT be used for gambling, as it can be influenced by a
/// malicious validator in the short term. It MAY be used in many
/// cryptographic protocols, however, so long as one remembers that this
/// (like everything else on-chain) is public. For example, it can be
/// used where a number is needed that cannot have been chosen by an
/// adversary, for purposes such as public-coin zero-knowledge proofs.
fn random() -> [u8; 32];
}
/// A type that can be used in runtime structures.
pub trait Member: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static {}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -63,11 +63,12 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
// Per convention: if the runtime behavior changes, increment spec_version and set impl_version
// to equal spec_version. If only runtime implementation changes and behavior does not, then
// leave spec_version as is and increment impl_version.
spec_version: 102,
impl_version: 104,
// Per convention: if the runtime behavior changes, increment spec_version
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 103,
impl_version: 103,
apis: RUNTIME_API_VERSIONS,
};
+364 -462
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -7,7 +7,7 @@ edition = "2018"
[dependencies]
hex-literal = "0.1.4"
parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] }
serde = { version = "1.0.90", optional = true }
serde = { version = "1.0.93", optional = true }
inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false }
rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false }
primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false }
@@ -16,12 +16,12 @@ system = { package = "srml-system", path = "../system", default-features = false
timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false }
session = { package = "srml-session", path = "../session", default-features = false }
babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false }
runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
[dev-dependencies]
lazy_static = "1.3.0"
parking_lot = "0.8.0"
substrate-primitives = { path = "../../core/primitives" }
runtime_io = { package = "sr-io", path = "../../core/sr-io" }
[features]
default = ["std"]
@@ -36,4 +36,5 @@ std = [
"inherents/std",
"babe-primitives/std",
"session/std",
"runtime_io/std",
]
+101 -13
View File
@@ -17,13 +17,14 @@
//! Consensus extension module for BABE consensus.
#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)]
pub use timestamp;
use rstd::{result, prelude::*};
use srml_support::{decl_storage, decl_module, StorageValue};
use srml_support::{decl_storage, decl_module, StorageValue, traits::FindAuthor};
use timestamp::{OnTimestampSet, Trait};
use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating}};
use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating, RandomnessBeacon}};
use primitives::ConsensusEngineId;
#[cfg(feature = "std")]
use timestamp::TimestampInherentData;
use parity_codec::{Encode, Decode};
@@ -31,8 +32,7 @@ use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent
#[cfg(feature = "std")]
use inherents::{InherentDataProviders, ProvideInherentData};
use babe_primitives::BABE_ENGINE_ID;
pub use babe_primitives::AuthorityId;
pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH, PUBLIC_KEY_LENGTH};
/// The BABE inherent identifier.
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot";
@@ -67,6 +67,7 @@ pub struct InherentDataProvider {
#[cfg(feature = "std")]
impl InherentDataProvider {
/// Constructs `Self`
pub fn new(slot_duration: u64) -> Self {
Self {
slot_duration
@@ -106,6 +107,9 @@ impl ProvideInherentData for InherentDataProvider {
}
}
/// The length of the BABE randomness
pub const RANDOMNESS_LENGTH: usize = 32;
decl_storage! {
trait Store for Module<T: Trait> as Babe {
/// The last timestamp.
@@ -113,11 +117,75 @@ decl_storage! {
/// The current authorities set.
Authorities get(authorities): Vec<AuthorityId>;
/// The epoch randomness.
///
/// # Security
///
/// This MUST NOT be used for gambling, as it can be influenced by a
/// malicious validator in the short term. It MAY be used in many
/// cryptographic protocols, however, so long as one remembers that this
/// (like everything else on-chain) it is public. For example, it can be
/// used where a number is needed that cannot have been chosen by an
/// adversary, for purposes such as public-coin zero-knowledge proofs.
EpochRandomness get(epoch_randomness): [u8; 32];
/// The randomness under construction
UnderConstruction: [u8; 32];
/// The randomness for the next epoch
NextEpochRandomness: [u8; 32];
/// The current epoch
EpochIndex get(epoch_index): u64;
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin { }
/// The BABE SRML module
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// Initialization
fn on_initialize() {
for i in Self::get_inherent_digests()
.logs
.iter()
.filter_map(|s| s.as_pre_runtime())
.filter_map(|(id, mut data)| if id == BABE_ENGINE_ID {
<[u8; VRF_OUTPUT_LENGTH]>::decode(&mut data)
} else {
None
}) {
Self::deposit_vrf_output(&i);
}
}
}
}
impl<T: Trait> RandomnessBeacon for Module<T> {
fn random() -> [u8; 32] {
Self::epoch_randomness()
}
}
/// A BABE public key
pub type BabeKey = [u8; PUBLIC_KEY_LENGTH];
impl<T: Trait> FindAuthor<u64> for Module<T> {
fn find_author<'a, I>(digests: I) -> Option<u64> where
I: 'a + IntoIterator<Item=(ConsensusEngineId, &'a [u8])>
{
for (id, mut data) in digests.into_iter() {
if id == BABE_ENGINE_ID {
let (_, _, i): (
[u8; VRF_OUTPUT_LENGTH],
[u8; VRF_PROOF_LENGTH],
u64,
) = Decode::decode(&mut data)?;
return Some(i)
}
}
return None
}
}
impl<T: Trait> Module<T> {
@@ -127,25 +195,31 @@ impl<T: Trait> Module<T> {
// the majority of their slot.
<timestamp::Module<T>>::minimum_period().saturating_mul(2.into())
}
}
impl<T: Trait> OnTimestampSet<T::Moment> for Module<T> {
fn on_timestamp_set(_moment: T::Moment) { }
}
impl<T: Trait> Module<T> {
fn change_authorities(new: Vec<AuthorityId>) {
Authorities::put(&new);
let log: DigestItem<T::Hash> = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
<system::Module<T>>::deposit_log(log.into());
}
fn deposit_vrf_output(vrf_output: &[u8; VRF_OUTPUT_LENGTH]) {
UnderConstruction::mutate(|z| z.iter_mut().zip(vrf_output).for_each(|(x, y)| *x^=y))
}
fn get_inherent_digests() -> system::DigestOf<T> {
<system::Module<T>>::digest()
}
}
impl<T: Trait> OnTimestampSet<T::Moment> for Module<T> {
fn on_timestamp_set(_moment: T::Moment) { }
}
impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
type Key = AuthorityId;
fn on_new_session<'a, I: 'a>(changed: bool, validators: I)
where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
{
// instant changes
if changed {
@@ -155,6 +229,20 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
Self::change_authorities(next_authorities);
}
}
let rho = UnderConstruction::get();
UnderConstruction::put([0; 32]);
let last_epoch_randomness = EpochRandomness::get();
let epoch_index = EpochIndex::get()
.checked_add(1)
.expect("epoch indices will never reach 2^64 before the death of the universe; qed");
EpochIndex::put(epoch_index);
EpochRandomness::put(NextEpochRandomness::get());
let mut s = [0; 72];
s[..32].copy_from_slice(&last_epoch_randomness);
s[32..40].copy_from_slice(&epoch_index.to_le_bytes());
s[40..].copy_from_slice(&rho);
NextEpochRandomness::put(runtime_io::blake2_256(&s))
}
fn on_disabled(_i: usize) {
// ignore?
+1 -1
View File
@@ -865,7 +865,7 @@ pub struct Schedule {
/// Base gas cost to call into a contract.
pub call_base_cost: Gas,
/// Base gas cost to instantiate a contract.
/// Base gas cost to instantiate a contract.
pub instantiate_base_cost: Gas,
/// Gas cost per one byte read from the sandbox memory.
+1 -1
View File
@@ -14,7 +14,7 @@ clap = { version = "~2.32", features = ["yaml"] }
tiny-bip39 = "0.6.0"
rustc-hex = "2.0"
substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39" }
schnorrkel = "0.1"
schnorrkel = "0.1.1"
hex = "0.3"
hex-literal = "0.2"
parity-codec = "4.1.1"