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 { impl SlotCompatible for AuraSlotCompatible {
fn extract_timestamp_and_slot( fn extract_timestamp_and_slot(
&self,
data: &InherentData data: &InherentData
) -> Result<(TimestampInherent, AuraInherent), consensus_common::Error> { ) -> Result<(TimestampInherent, AuraInherent, std::time::Duration), consensus_common::Error> {
data.timestamp_inherent_data() data.timestamp_inherent_data()
.and_then(|t| data.aura_inherent_data().map(|a| (t, a))) .and_then(|t| data.aura_inherent_data().map(|a| (t, a)))
.map_err(Into::into) .map_err(Into::into)
.map_err(consensus_common::Error::InherentData) .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, select_chain,
worker, worker,
sync_oracle, 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>>, mut body: Option<Vec<B::Extrinsic>>,
) -> Result<(ImportBlock<B>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> { ) -> 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 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))?; .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?;
let hash = header.hash(); let hash = header.hash();
let parent_hash = *header.parent_hash(); let parent_hash = *header.parent_hash();
@@ -31,6 +31,15 @@ pub type AuthorityId = Public;
/// The `ConsensusEngineId` of BABE. /// The `ConsensusEngineId` of BABE.
pub const BABE_ENGINE_ID: ConsensusEngineId = *b"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. /// Configuration data used by the BABE consensus engine.
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)] #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)]
pub struct BabeConfiguration { pub struct BabeConfiguration {
+14 -13
View File
@@ -16,15 +16,12 @@
//! Private implementation details of BABE digests. //! Private implementation details of BABE digests.
use primitives::sr25519::{Public, Signature}; use primitives::sr25519::Signature;
use babe_primitives::BABE_ENGINE_ID; use babe_primitives::{self, BABE_ENGINE_ID};
use runtime_primitives::{DigestItem, generic::OpaqueDigestItemId}; use runtime_primitives::{DigestItem, generic::OpaqueDigestItemId};
use std::fmt::Debug; use std::fmt::Debug;
use parity_codec::{Decode, Encode, Codec, Input}; use parity_codec::{Decode, Encode, Codec, Input};
use schnorrkel::{ use schnorrkel::{vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}};
vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH},
PUBLIC_KEY_LENGTH,
};
/// A BABE pre-digest. It includes: /// A BABE pre-digest. It includes:
/// ///
@@ -36,26 +33,26 @@ use schnorrkel::{
pub struct BabePreDigest { pub struct BabePreDigest {
pub(super) vrf_output: VRFOutput, pub(super) vrf_output: VRFOutput,
pub(super) proof: VRFProof, pub(super) proof: VRFProof,
pub(super) author: Public, pub(super) index: u64,
pub(super) slot_num: u64, pub(super) slot_num: u64,
} }
/// The prefix used by BABE for its VRF keys. /// The prefix used by BABE for its VRF keys.
pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf"; pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf";
type TmpDecode = ( type RawBabePreDigest = (
[u8; VRF_OUTPUT_LENGTH], [u8; VRF_OUTPUT_LENGTH],
[u8; VRF_PROOF_LENGTH], [u8; VRF_PROOF_LENGTH],
[u8; PUBLIC_KEY_LENGTH], u64,
u64, u64,
); );
impl Encode for BabePreDigest { impl Encode for BabePreDigest {
fn encode(&self) -> Vec<u8> { fn encode(&self) -> Vec<u8> {
let tmp: TmpDecode = ( let tmp: RawBabePreDigest = (
*self.vrf_output.as_bytes(), *self.vrf_output.as_bytes(),
self.proof.to_bytes(), self.proof.to_bytes(),
self.author.0, self.index,
self.slot_num, self.slot_num,
); );
parity_codec::Encode::encode(&tmp) parity_codec::Encode::encode(&tmp)
@@ -64,11 +61,15 @@ impl Encode for BabePreDigest {
impl Decode for BabePreDigest { impl Decode for BabePreDigest {
fn decode<R: Input>(i: &mut R) -> Option<Self> { 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 { Some(BabePreDigest {
proof: VRFProof::from_bytes(&proof).ok()?, proof: VRFProof::from_bytes(&proof).ok()?,
vrf_output: VRFOutput::from_bytes(&output).ok()?, vrf_output: VRFOutput::from_bytes(&output).ok()?,
author: Public(public_key), index,
slot_num, slot_num,
}) })
} }
+57 -46
View File
@@ -23,7 +23,8 @@
//! This crate is highly unstable and experimental. Breaking changes may //! This crate is highly unstable and experimental. Breaking changes may
//! happen at any point. This crate is also missing features, such as banning //! happen at any point. This crate is also missing features, such as banning
//! of malicious validators, that are essential for a production network. //! 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; extern crate core;
mod digest; mod digest;
use digest::CompatibleDigestItem; use digest::CompatibleDigestItem;
@@ -120,17 +121,17 @@ impl Config {
} }
} }
struct BabeSlotCompatible; impl SlotCompatible for BabeLink {
impl SlotCompatible for BabeSlotCompatible {
fn extract_timestamp_and_slot( fn extract_timestamp_and_slot(
data: &InherentData &self,
) -> Result<(TimestampInherent, u64), consensus_common::Error> { data: &InherentData,
) -> Result<(TimestampInherent, u64, std::time::Duration), consensus_common::Error> {
trace!(target: "babe", "extract timestamp"); trace!(target: "babe", "extract timestamp");
data.timestamp_inherent_data() data.timestamp_inherent_data()
.and_then(|t| data.babe_inherent_data().map(|a| (t, a))) .and_then(|t| data.babe_inherent_data().map(|a| (t, a)))
.map_err(Into::into) .map_err(Into::into)
.map_err(consensus_common::Error::InherentData) .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 /// Force authoring of blocks even if we are offline
pub force_authoring: bool, 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. /// 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, sync_oracle,
inherent_data_providers, inherent_data_providers,
force_authoring, force_authoring,
time_source,
}: BabeParams<C, E, I, SO, SC>) -> Result< }: BabeParams<C, E, I, SO, SC>) -> Result<
impl Future<Item=(), Error=()>, impl Future<Item=(), Error=()>,
consensus_common::Error, consensus_common::Error,
@@ -203,12 +208,13 @@ pub fn start_babe<B, C, SC, E, I, SO, Error, H>(BabeParams {
threshold: config.threshold(), threshold: config.threshold(),
}; };
register_babe_inherent_data_provider(&inherent_data_providers, config.0.slot_duration())?; 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, config.0,
select_chain, select_chain,
worker, worker,
sync_oracle, 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 // FIXME replace the dummy empty slices with real data
// https://github.com/paritytech/substrate/issues/2435 // https://github.com/paritytech/substrate/issues/2435
// https://github.com/paritytech/substrate/issues/2436 // 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], &[0u8; 0],
slot_info.number, slot_info.number,
&[0u8; 0], &[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 { let inherent_digest = BabePreDigest {
proof, proof,
vrf_output: inout.to_output(), vrf_output: inout.to_output(),
author: pair.public(), index: index as u64,
slot_num, slot_num,
}; };
@@ -459,17 +465,17 @@ fn check_header<B: Block + Sized, C: AuxStore>(
})?; })?;
let pre_digest = find_pre_digest::<B>(&header)?; 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 { if slot_num > slot_now {
header.digest_mut().push(seal); header.digest_mut().push(seal);
Ok(CheckedHeader::Deferred(header, slot_num)) 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")) Err(babe_err!("Slot author not found"))
} else { } 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 (inout, _batchable_proof) = {
let transcript = make_transcript( let transcript = make_transcript(
Default::default(), 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. /// A verifier for Babe blocks.
pub struct BabeVerifier<C> { pub struct BabeVerifier<C> {
client: Arc<C>, client: Arc<C>,
inherent_data_providers: inherents::InherentDataProviders, inherent_data_providers: inherents::InherentDataProviders,
config: Config, config: Config,
timestamps: Mutex<(Option<Duration>, Vec<(Instant, u64)>)>, time_source: BabeLink,
} }
impl<C> BabeVerifier<C> { impl<C> BabeVerifier<C> {
@@ -551,11 +561,11 @@ fn median_algorithm(
slot_duration: u64, slot_duration: u64,
slot_num: u64, slot_num: u64,
slot_now: 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 { 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) let offset: u128 = u128::from(slot_duration)
.checked_mul(1_000_000u128) // self.config.get() returns *milliseconds* .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)))) .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 let &median = new_list
.get(num_timestamps / 2) .get(num_timestamps / 2)
.expect("we have at least one timestamp, so this is a valid index; qed"); .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 // FIXME #2927: pass this to the block authoring logic somehow
timestamps.0.replace(Instant::now() - median); time_source.0.replace(Instant::now() - median);
} else { } 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 .inherent_data_providers
.create_inherent_data() .create_inherent_data()
.map_err(String::from)?; .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))?; .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?;
let hash = header.hash(); let hash = header.hash();
let parent_hash = *header.parent_hash(); let parent_hash = *header.parent_hash();
@@ -672,7 +682,7 @@ impl<B: Block, C> Verifier<B> for BabeVerifier<C> where
self.config.get(), self.config.get(),
slot_num, slot_num,
slot_now, slot_now,
&mut *self.timestamps.lock(), &mut *self.time_source.0.lock(),
); );
// FIXME #1019 extract authorities // FIXME #1019 extract authorities
Ok((import_block, maybe_keys)) Ok((import_block, maybe_keys))
@@ -765,8 +775,9 @@ fn claim_slot(
authorities: &[AuthorityId], authorities: &[AuthorityId],
key: &sr25519::Pair, key: &sr25519::Pair,
threshold: u64, threshold: u64,
) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> { ) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize)> {
if !authorities.contains(&key.public()) { return None } let public = &key.public();
let index = authorities.iter().position(|s| s == public)?;
let transcript = make_transcript( let transcript = make_transcript(
randomness, randomness,
slot_number, slot_number,
@@ -780,7 +791,9 @@ fn claim_slot(
// be empty. Therefore, this division is safe. // be empty. Therefore, this division is safe.
let threshold = threshold / authorities.len() as u64; 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 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>>, finality_proof_request_builder: Option<SharedFinalityProofRequestBuilder<B>>,
client: Arc<C>, client: Arc<C>,
inherent_data_providers: InherentDataProviders, inherent_data_providers: InherentDataProviders,
) -> Result<BabeImportQueue<B>, consensus_common::Error> where ) -> Result<(BabeImportQueue<B>, BabeLink), consensus_common::Error> where
B: Block, B: Block,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore, C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B> + BabeApi<B>, 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())?; register_babe_inherent_data_provider(&inherent_data_providers, config.get())?;
initialize_authorities_cache(&*client)?; initialize_authorities_cache(&*client)?;
let verifier = Arc::new( let verifier = BabeVerifier {
BabeVerifier { client: client,
client: client, inherent_data_providers,
inherent_data_providers, time_source: Default::default(),
timestamps: Default::default(), config,
config, };
} let timestamp_core = verifier.time_source.clone();
); Ok((BasicQueue::new(
Ok(BasicQueue::new( Arc::new(verifier),
verifier,
block_import, block_import,
justification_import, justification_import,
finality_proof_import, finality_proof_import,
finality_proof_request_builder, finality_proof_request_builder,
)) ), timestamp_core))
} }
#[cfg(test)]
#[allow(dead_code, unused_imports, deprecated)]
// FIXME #2532: need to allow deprecated until refactor is done // FIXME #2532: need to allow deprecated until refactor is done
// https://github.com/paritytech/substrate/issues/2532 // https://github.com/paritytech/substrate/issues/2532
#[cfg(test)]
#[allow(unused_imports, deprecated)]
#[cfg_attr(test, allow(dead_code))]
mod tests { mod tests {
use super::*; use super::*;
@@ -947,7 +959,7 @@ mod tests {
client, client,
inherent_data_providers, inherent_data_providers,
config, config,
timestamps: Default::default(), time_source: Default::default(),
}) })
} }
@@ -1028,7 +1040,7 @@ mod tests {
#[allow(deprecated)] #[allow(deprecated)]
let select_chain = LongestChain::new(client.backend().clone()); let select_chain = LongestChain::new(client.backend().clone());
let babe = start_babe(BabeParams { runtime.spawn(start_babe(BabeParams {
config, config,
local_key: Arc::new(key.clone().into()), local_key: Arc::new(key.clone().into()),
block_import: client.clone(), block_import: client.clone(),
@@ -1038,9 +1050,8 @@ mod tests {
sync_oracle: DummyOracle, sync_oracle: DummyOracle,
inherent_data_providers, inherent_data_providers,
force_authoring: false, force_authoring: false,
}).expect("Starts babe"); time_source: Default::default(),
}).expect("Starts babe"));
runtime.spawn(babe);
} }
debug!(target: "babe", "checkpoint 5"); debug!(target: "babe", "checkpoint 5");
+12 -7
View File
@@ -48,10 +48,6 @@ pub trait SlotWorker<B: Block> {
/// triggered. /// triggered.
type OnSlot: IntoFuture<Item = (), Error = consensus_common::Error>; 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. /// Called when a new slot is triggered.
fn on_slot(&self, chain_head: B::Header, slot_info: SlotInfo) -> Self::OnSlot; 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 { pub trait SlotCompatible {
/// Extract timestamp and slot from inherent data. /// Extract timestamp and slot from inherent data.
fn extract_timestamp_and_slot( fn extract_timestamp_and_slot(
&self,
inherent: &InherentData, 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. /// Start a new slot worker.
@@ -74,6 +75,7 @@ pub fn start_slot_worker<B, C, W, T, SO, SC>(
worker: W, worker: W,
sync_oracle: SO, sync_oracle: SO,
inherent_data_providers: InherentDataProviders, inherent_data_providers: InherentDataProviders,
timestamp_extractor: SC,
) -> impl Future<Item = (), Error = ()> ) -> impl Future<Item = (), Error = ()>
where where
B: Block, B: Block,
@@ -86,8 +88,11 @@ where
let SlotDuration(slot_duration) = slot_duration; let SlotDuration(slot_duration) = slot_duration;
// rather than use a timer interval, we schedule our waits ourselves // rather than use a timer interval, we schedule our waits ourselves
let mut authorship = Slots::<SC>::new(slot_duration.slot_duration(), inherent_data_providers) let mut authorship = Slots::<SC>::new(
.map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e)) slot_duration.slot_duration(),
inherent_data_providers,
timestamp_extractor,
).map_err(|e| debug!(target: "slots", "Faulty timer: {:?}", e))
.for_each(move |slot_info| { .for_each(move |slot_info| {
// only propose when we are not syncing. // only propose when we are not syncing.
if sync_oracle.is_major_syncing() { if sync_oracle.is_major_syncing() {
+47 -44
View File
@@ -24,7 +24,6 @@ use futures::prelude::*;
use futures::try_ready; use futures::try_ready;
use inherents::{InherentData, InherentDataProviders}; use inherents::{InherentData, InherentDataProviders};
use std::marker::PhantomData;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio_timer::Delay; use tokio_timer::Delay;
@@ -55,11 +54,11 @@ impl SignedDuration {
/// Get the slot for now. Panics if `slot_duration` is 0. /// Get the slot for now. Panics if `slot_duration` is 0.
pub fn slot_now(&self, slot_duration: u64) -> u64 { pub fn slot_now(&self, slot_duration: u64) -> u64 {
if self.is_positive { (if self.is_positive {
duration_now() + self.offset duration_now() + self.offset
} else { } else {
duration_now() - self.offset duration_now() - self.offset
}.as_secs() / slot_duration }.as_secs()) / slot_duration
} }
} }
@@ -102,18 +101,22 @@ pub struct Slots<SC> {
slot_duration: u64, slot_duration: u64,
inner_delay: Option<Delay>, inner_delay: Option<Delay>,
inherent_data_providers: InherentDataProviders, inherent_data_providers: InherentDataProviders,
_marker: PhantomData<SC>, timestamp_extractor: SC,
} }
impl<SC> Slots<SC> { impl<SC> Slots<SC> {
/// Create a new `Slots` stream. /// 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 { Slots {
last_slot: 0, last_slot: 0,
slot_duration, slot_duration,
inner_delay: None, inner_delay: None,
inherent_data_providers, inherent_data_providers,
_marker: PhantomData, timestamp_extractor,
} }
} }
} }
@@ -123,49 +126,49 @@ impl<SC: SlotCompatible> Stream for Slots<SC> {
type Error = Error; type Error = Error;
fn poll(&mut self) -> Poll<Option<SlotInfo>, Self::Error> { fn poll(&mut self) -> Poll<Option<SlotInfo>, Self::Error> {
let slot_duration = self.slot_duration; loop {
self.inner_delay = match self.inner_delay.take() { let slot_duration = self.slot_duration;
None => { self.inner_delay = match self.inner_delay.take() {
// schedule wait. None => {
let wait_until = Instant::now() + time_until_next(duration_now(), slot_duration); // schedule wait.
Some(Delay::new(wait_until)) 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 { // timeout has fired.
try_ready!(inner_delay
.poll()
.map_err(Error::FaultyTimer));
}
// 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 // never yield the same slot twice.
.inherent_data_providers if slot_num > self.last_slot {
.create_inherent_data() self.last_slot = slot_num;
.map_err(|s| consensus_common::Error::InherentData(s.into_owned()))?;
let (timestamp, slot_num) = SC::extract_timestamp_and_slot(&inherent_data)?;
// reschedule delay for next slot. break Ok(Async::Ready(Some(SlotInfo {
let ends_at = number: slot_num,
Instant::now() + time_until_next(Duration::from_secs(timestamp), slot_duration); duration: self.slot_duration,
self.inner_delay = Some(Delay::new(ends_at)); timestamp,
ends_at,
// never yield the same slot twice. inherent_data,
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()
} }
} }
} }
+17 -17
View File
@@ -15,7 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@@ -33,12 +33,12 @@ dependencies = [
[[package]] [[package]]
name = "hash-db" name = "hash-db"
version = "0.12.2" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "hash256-std-hasher" name = "hash256-std-hasher"
version = "0.12.2" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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-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)", "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)", "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]] [[package]]
@@ -157,14 +157,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.91" version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "sr-io" name = "sr-io"
version = "2.0.0" version = "2.0.0"
dependencies = [ 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)", "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)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-std 2.0.0", "sr-std 2.0.0",
@@ -197,9 +197,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "substrate-primitives" name = "substrate-primitives"
version = "2.0.0" version = "2.0.0"
dependencies = [ 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)",
"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)",
"hash256-std-hasher 0.12.2 (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)", "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)", "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)", "primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -209,7 +209,7 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "0.15.34" version = "0.15.39"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -222,7 +222,7 @@ name = "toml"
version = "0.5.1" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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]] [[package]]
@@ -230,7 +230,7 @@ name = "uint"
version = "0.8.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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)", "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)", "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] [metadata]
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "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 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 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 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 hash-db 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c95a428c86ed4633d83e07ef9e0a147a906da01e931f07e74a85bedce5a43"
"checksum hash256-std-hasher 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8b2027c19ec91eb304999abae7307d225cf93be42af53b0039f76e98ed5af86" "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 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 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" "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 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 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 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 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 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 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" "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 } ed25519-dalek = { version = "1.0.0-pre.1", optional = true }
base58 = { version = "0.1", optional = true } base58 = { version = "0.1", optional = true }
blake2-rfc = { version = "0.2.18", 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 } rand = { version = "0.6", optional = true }
sha2 = { version = "0.8", optional = true } sha2 = { version = "0.8", optional = true }
substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39", optional = true } substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39", optional = true }
@@ -594,6 +594,21 @@ pub trait MaybeHash {}
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
impl<T> MaybeHash for T {} 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. /// A type that can be used in runtime structures.
pub trait Member: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static {} 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"), spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"), impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10, authoring_version: 10,
// Per convention: if the runtime behavior changes, increment spec_version and set impl_version // Per convention: if the runtime behavior changes, increment spec_version
// to equal spec_version. If only runtime implementation changes and behavior does not, then // and set impl_version to equal spec_version. If only runtime
// leave spec_version as is and increment impl_version. // implementation changes and behavior does not, then leave spec_version as
spec_version: 102, // is and increment impl_version.
impl_version: 104, spec_version: 103,
impl_version: 103,
apis: RUNTIME_API_VERSIONS, 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] [dependencies]
hex-literal = "0.1.4" hex-literal = "0.1.4"
parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } 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 } inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false }
rstd = { package = "sr-std", path = "../../core/sr-std", 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 } 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 } timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false }
session = { package = "srml-session", path = "../session", 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 } 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] [dev-dependencies]
lazy_static = "1.3.0" lazy_static = "1.3.0"
parking_lot = "0.8.0" parking_lot = "0.8.0"
substrate-primitives = { path = "../../core/primitives" } substrate-primitives = { path = "../../core/primitives" }
runtime_io = { package = "sr-io", path = "../../core/sr-io" }
[features] [features]
default = ["std"] default = ["std"]
@@ -36,4 +36,5 @@ std = [
"inherents/std", "inherents/std",
"babe-primitives/std", "babe-primitives/std",
"session/std", "session/std",
"runtime_io/std",
] ]
+101 -13
View File
@@ -17,13 +17,14 @@
//! Consensus extension module for BABE consensus. //! Consensus extension module for BABE consensus.
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)] #![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)]
pub use timestamp; pub use timestamp;
use rstd::{result, prelude::*}; 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 timestamp::{OnTimestampSet, Trait};
use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating}}; use primitives::{generic::DigestItem, traits::{SaturatedConversion, Saturating, RandomnessBeacon}};
use primitives::ConsensusEngineId;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use timestamp::TimestampInherentData; use timestamp::TimestampInherentData;
use parity_codec::{Encode, Decode}; use parity_codec::{Encode, Decode};
@@ -31,8 +32,7 @@ use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent
#[cfg(feature = "std")] #[cfg(feature = "std")]
use inherents::{InherentDataProviders, ProvideInherentData}; use inherents::{InherentDataProviders, ProvideInherentData};
use babe_primitives::BABE_ENGINE_ID; use babe_primitives::BABE_ENGINE_ID;
pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH, PUBLIC_KEY_LENGTH};
pub use babe_primitives::AuthorityId;
/// The BABE inherent identifier. /// The BABE inherent identifier.
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot"; pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot";
@@ -67,6 +67,7 @@ pub struct InherentDataProvider {
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl InherentDataProvider { impl InherentDataProvider {
/// Constructs `Self`
pub fn new(slot_duration: u64) -> Self { pub fn new(slot_duration: u64) -> Self {
Self { Self {
slot_duration slot_duration
@@ -106,6 +107,9 @@ impl ProvideInherentData for InherentDataProvider {
} }
} }
/// The length of the BABE randomness
pub const RANDOMNESS_LENGTH: usize = 32;
decl_storage! { decl_storage! {
trait Store for Module<T: Trait> as Babe { trait Store for Module<T: Trait> as Babe {
/// The last timestamp. /// The last timestamp.
@@ -113,11 +117,75 @@ decl_storage! {
/// The current authorities set. /// The current authorities set.
Authorities get(authorities): Vec<AuthorityId>; 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! { 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> { impl<T: Trait> Module<T> {
@@ -127,25 +195,31 @@ impl<T: Trait> Module<T> {
// the majority of their slot. // the majority of their slot.
<timestamp::Module<T>>::minimum_period().saturating_mul(2.into()) <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>) { fn change_authorities(new: Vec<AuthorityId>) {
Authorities::put(&new); Authorities::put(&new);
let log: DigestItem<T::Hash> = DigestItem::Consensus(BABE_ENGINE_ID, new.encode()); let log: DigestItem<T::Hash> = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
<system::Module<T>>::deposit_log(log.into()); <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> { impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
type Key = AuthorityId; type Key = AuthorityId;
fn on_new_session<'a, I: 'a>(changed: bool, validators: I) 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 // instant changes
if changed { if changed {
@@ -155,6 +229,20 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
Self::change_authorities(next_authorities); 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) { fn on_disabled(_i: usize) {
// ignore? // ignore?
+1 -1
View File
@@ -865,7 +865,7 @@ pub struct Schedule {
/// Base gas cost to call into a contract. /// Base gas cost to call into a contract.
pub call_base_cost: Gas, pub call_base_cost: Gas,
/// Base gas cost to instantiate a contract. /// Base gas cost to instantiate a contract.
pub instantiate_base_cost: Gas, pub instantiate_base_cost: Gas,
/// Gas cost per one byte read from the sandbox memory. /// 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" tiny-bip39 = "0.6.0"
rustc-hex = "2.0" rustc-hex = "2.0"
substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39" } substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39" }
schnorrkel = "0.1" schnorrkel = "0.1.1"
hex = "0.3" hex = "0.3"
hex-literal = "0.2" hex-literal = "0.2"
parity-codec = "4.1.1" parity-codec = "4.1.1"