diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 1e3e42560a..90761c3807 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -2892,23 +2892,6 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "schnorrkel" -version = "0.1.1" -source = "git+https://github.com/paritytech/schnorrkel#1762df02ac48ba5c3fa8c162e19a393247079f88" -dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "merlin 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "schnorrkel" version = "0.1.1" @@ -3271,6 +3254,29 @@ dependencies = [ "substrate-primitives 1.0.0", ] +[[package]] +name = "srml-babe" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 1.0.0", + "sr-primitives 1.0.0", + "sr-std 1.0.0", + "srml-consensus 1.0.0", + "srml-session 1.0.0", + "srml-staking 1.0.0", + "srml-support 1.0.0", + "srml-system 1.0.0", + "srml-timestamp 1.0.0", + "substrate-consensus-babe-primitives 1.0.0", + "substrate-inherents 1.0.0", + "substrate-primitives 1.0.0", +] + [[package]] name = "srml-balances" version = "1.0.0" @@ -3936,13 +3942,16 @@ dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (git+https://github.com/paritytech/schnorrkel)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 1.0.0", "sr-primitives 1.0.0", "sr-version 1.0.0", + "srml-babe 0.1.0", "srml-consensus 1.0.0", "srml-support 1.0.0", "substrate-client 1.0.0", @@ -4450,6 +4459,7 @@ dependencies = [ "substrate-client 1.0.0", "substrate-consensus-aura-primitives 1.0.0", "substrate-consensus-authorities 1.0.0", + "substrate-consensus-babe-primitives 1.0.0", "substrate-executor 1.0.0", "substrate-inherents 1.0.0", "substrate-keyring 1.0.0", @@ -5671,7 +5681,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" -"checksum schnorrkel 0.1.1 (git+https://github.com/paritytech/schnorrkel)" = "" "checksum schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5eff518f9bed3d803a0d002af0ab96339b0ebbedde3bec98a684986134b7a39" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum secp256k1 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfaccd3a23619349e0878d9a241f34b1982343cdf67367058cd7d078d326b63e" diff --git a/substrate/core/consensus/aura/primitives/Cargo.toml b/substrate/core/consensus/aura/primitives/Cargo.toml index 759dfae37c..ae59ea70a9 100644 --- a/substrate/core/consensus/aura/primitives/Cargo.toml +++ b/substrate/core/consensus/aura/primitives/Cargo.toml @@ -12,5 +12,6 @@ runtime_primitives = { package = "sr-primitives", path = "../../../sr-primitives [features] default = ["std"] std = [ + "runtime_primitives/std", "substrate-client/std", ] diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs index 784872f6af..3783f18d0c 100644 --- a/substrate/core/consensus/aura/src/lib.rs +++ b/substrate/core/consensus/aura/src/lib.rs @@ -62,10 +62,10 @@ use srml_aura::{ }; use substrate_telemetry::{telemetry, CONSENSUS_TRACE, CONSENSUS_DEBUG, CONSENSUS_WARN, CONSENSUS_INFO}; -use slots::{CheckedHeader, SlotWorker, SlotInfo, SlotCompatible}; +use slots::{CheckedHeader, SlotWorker, SlotInfo, SlotCompatible, slot_now}; pub use aura_primitives::*; -pub use consensus_common::SyncOracle; +pub use consensus_common::{SyncOracle, ExtraVerification}; type AuthorityId

=

::Public; type Signature

=

::Signature; @@ -120,20 +120,6 @@ fn slot_author(slot_num: u64, authorities: &[AuthorityId

]) -> Option Some(current_author) } -fn duration_now() -> Option { - use std::time::SystemTime; - - let now = SystemTime::now(); - now.duration_since(SystemTime::UNIX_EPOCH).map_err(|e| { - warn!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e); - }).ok() -} - -/// Get the slot for now. -fn slot_now(slot_duration: u64) -> Option { - duration_now().map(|s| s.as_secs() / slot_duration) -} - fn inherent_to_common_error(err: RuntimeString) -> consensus_common::Error { consensus_common::ErrorKind::InherentData(err.into()).into() } @@ -467,7 +453,6 @@ impl SlotWorker for AuraWorker( slot_now: u64, mut header: B::Header, @@ -517,19 +502,6 @@ fn check_header( } } -/// Extra verification for Aura blocks. -pub trait ExtraVerification: Send + Sync { - /// Future that resolves when the block is verified or fails with error if not. - type Verified: IntoFuture; - - /// Do additional verification for this block. - fn verify( - &self, - header: &B::Header, - body: Option<&[B::Extrinsic]>, - ) -> Self::Verified; -} - /// A verifier for Aura blocks. pub struct AuraVerifier { client: Arc, @@ -785,7 +757,8 @@ pub fn import_queue( /// Start an import queue for the Aura consensus algorithm with backwards compatibility. #[deprecated( - note = "should not be used unless backwards compatibility with an older chain is needed." + since = "1.0.1", + note = "should not be used unless backwards compatibility with an older chain is needed.", )] pub fn import_queue_accept_old_seals( slot_duration: SlotDuration, @@ -828,8 +801,8 @@ mod tests { use network::config::ProtocolConfig; use parking_lot::Mutex; use tokio::runtime::current_thread; - use keyring::ed25519::Keyring; - use primitives::ed25519; + use keyring::sr25519::Keyring; + use primitives::sr25519; use client::BlockchainEvents; use test_client; @@ -844,7 +817,7 @@ mod tests { type Proposer = DummyProposer; type Error = Error; - fn init(&self, parent_header: &::Header, _authorities: &[AuthorityId]) + fn init(&self, parent_header: &::Header, _authorities: &[AuthorityId]) -> Result { Ok(DummyProposer(parent_header.number + 1, self.0.clone())) @@ -870,7 +843,7 @@ mod tests { impl TestNetFactory for AuraTestNet { type Specialization = DummySpecialization; - type Verifier = AuraVerifier; + type Verifier = AuraVerifier; type PeerData = (); /// Create new test network with peers and given config. @@ -957,7 +930,7 @@ mod tests { &inherent_data_providers, slot_duration.get() ).expect("Registers aura inherent data provider"); - let aura = start_aura::<_, _, _, _, ed25519::Pair, _, _, _>( + let aura = start_aura::<_, _, _, _, sr25519::Pair, _, _, _>( slot_duration, Arc::new(key.clone().into()), client.clone(), diff --git a/substrate/core/consensus/babe/Cargo.toml b/substrate/core/consensus/babe/Cargo.toml index 7cd787e02c..8d3fada183 100644 --- a/substrate/core/consensus/babe/Cargo.toml +++ b/substrate/core/consensus/babe/Cargo.toml @@ -16,6 +16,7 @@ runtime_io = { package = "sr-io", path = "../../sr-io" } inherents = { package = "substrate-inherents", path = "../../inherents" } srml-consensus = { path = "../../../srml/consensus" } substrate-telemetry = { path = "../../telemetry" } +srml-babe = { path = "../../../srml/babe" } client = { package = "substrate-client", path = "../../client" } consensus_common = { package = "substrate-consensus-common", path = "../common" } authorities = { package = "substrate-consensus-authorities", path = "../authorities" } @@ -26,10 +27,9 @@ tokio = "0.1.18" parking_lot = "0.7.1" error-chain = "0.12.0" log = "0.4.6" - -[dependencies.schnorrkel] -git = "https://github.com/paritytech/schnorrkel" -branch = "master" +schnorrkel = "0.1.1" +rand = "0.6.5" +merlin = "1.0.3" [dev-dependencies] keyring = { package = "substrate-keyring", path = "../../keyring" } diff --git a/substrate/core/consensus/babe/primitives/Cargo.toml b/substrate/core/consensus/babe/primitives/Cargo.toml index 3abe7d5e6e..2f9147054f 100644 --- a/substrate/core/consensus/babe/primitives/Cargo.toml +++ b/substrate/core/consensus/babe/primitives/Cargo.toml @@ -9,11 +9,13 @@ edition = "2018" substrate-client = { path = "../../../client", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../../../sr-primitives", default-features = false } slots = { package = "substrate-consensus-slots", path = "../../slots", optional = true } -parity-codec = "^3.4.0" +parity-codec = { version = "3.5.1", default-features = false } [features] default = ["std"] std = [ + "runtime_primitives/std", "substrate-client/std", + "parity-codec/std", "slots", ] diff --git a/substrate/core/consensus/babe/primitives/src/lib.rs b/substrate/core/consensus/babe/primitives/src/lib.rs index 4f83cce5bd..83ba6ccba2 100644 --- a/substrate/core/consensus/babe/primitives/src/lib.rs +++ b/substrate/core/consensus/babe/primitives/src/lib.rs @@ -29,31 +29,28 @@ pub const BABE_ENGINE_ID: ConsensusEngineId = [b'b', b'a', b'b', b'e']; /// Configuration data used by the BABE consensus engine. #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)] pub struct BabeConfiguration { - slot_duration: u64, - expected_block_time: u64, -} - -impl BabeConfiguration { - /// Return the expected block time in milliseconds for BABE. Currently, - /// only the value provided by this type at genesis will be used. - /// - /// Dynamic expected block time may be supported in the future. - pub fn expected_block_time(&self) -> u64 { - self.expected_block_time - } - - /// Return the slot duration in milliseconds for BABE. Currently, only + /// The slot duration in milliseconds for BABE. Currently, only /// the value provided by this type at genesis will be used. /// /// Dynamic slot duration may be supported in the future. - pub fn slot_duration(&self) -> u64 { - self.slot_duration - } + pub slot_duration: u64, + + /// The expected block time in milliseconds for BABE. Currently, + /// only the value provided by this type at genesis will be used. + /// + /// Dynamic expected block time may be supported in the future. + pub expected_block_time: u64, + + /// The maximum permitted VRF output, or *threshold*, for BABE. Currently, + /// only the value provided by this type at genesis will be used. + /// + /// Dynamic thresholds may be supported in the future. + pub threshold: u64, } #[cfg(feature = "std")] impl slots::SlotData for BabeConfiguration { - /// Return the slot duration in milliseconds for BABE. Currently, only + /// Return the slot duration in milliseconds for BABE. Currently, only /// the value provided by this type at genesis will be used. /// /// Dynamic slot duration may be supported in the future. @@ -67,7 +64,7 @@ impl slots::SlotData for BabeConfiguration { decl_runtime_apis! { /// API necessary for block authorship with BABE. pub trait BabeApi { - /// Return the configuration for BABE. Currently, + /// Return the configuration for BABE. Currently, /// only the value provided by this type at genesis will be used. /// /// Dynamic configuration may be supported in the future. diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs index 0881f8bb97..c86e14de31 100644 --- a/substrate/core/consensus/babe/src/lib.rs +++ b/substrate/core/consensus/babe/src/lib.rs @@ -14,58 +14,141 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +//! # BABE consensus +//! //! BABE (Blind Assignment for Blockchain Extension) consensus in substrate. -#![forbid(warnings, unsafe_code, missing_docs)] - +//! +//! # Stability +//! +//! 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)] +#![deny(warnings)] +extern crate core; pub use babe_primitives::*; -use parity_codec::{Decode, Encode, Input}; -use runtime_primitives::generic; -use primitives::sr25519::{ - Public, - Signature, - LocalizedSignature, +pub use consensus_common::SyncOracle; +use consensus_common::ExtraVerification; +use runtime_primitives::{generic, generic::BlockId, Justification}; +use runtime_primitives::traits::{ + Block, Header, Digest, DigestItemFor, DigestItem, ProvideRuntimeApi, AuthorityIdFor, }; - +use std::{sync::Arc, u64, fmt::Debug}; +use parity_codec::{Decode, Encode, Input}; +use primitives::{ + crypto::Pair, + sr25519::{Public, Signature, LocalizedSignature, self}, +}; +use merlin::Transcript; +use inherents::{InherentDataProviders, InherentData, RuntimeString}; +use substrate_telemetry::{telemetry, CONSENSUS_TRACE, CONSENSUS_DEBUG, CONSENSUS_WARN, CONSENSUS_INFO}; use schnorrkel::{ - SecretKey as Secret, - vrf::{VRFProof, VRF_PROOF_LENGTH}, + keys::Keypair, + vrf::{ + VRFProof, VRFProofBatchable, VRFInOut, VRFOutput, + VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH, + }, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, }; +use authorities::AuthoritiesApi; +use consensus_common::{self, Authorities, BlockImport, Environment, Proposer, + ForkChoiceStrategy, ImportBlock, BlockOrigin, Error as ConsensusError, +}; +use srml_babe::{ + BabeInherentData, + timestamp::{TimestampInherentData, InherentType as TimestampInherent} +}; +use consensus_common::well_known_cache_keys; +use consensus_common::import_queue::{Verifier, BasicQueue}; +use client::{ + ChainHead, + block_builder::api::BlockBuilder as BlockBuilderApi, + blockchain::ProvideCache, + runtime_api::ApiExt, + error::Result as CResult, + backend::AuxStore, +}; +use slots::CheckedHeader; +use futures::{Future, IntoFuture, future}; +use tokio::timer::Timeout; +use log::{error, warn, debug, info, trace}; + +use slots::{SlotWorker, SlotInfo, SlotCompatible, slot_now}; /// A BABE seal. It includes: -/// +/// /// * The public key /// * The VRF proof /// * The signature /// * The slot number #[derive(Clone, Debug, PartialEq, Eq)] pub struct BabeSeal { + vrf_output: VRFOutput, proof: VRFProof, signature: LocalizedSignature, slot_num: u64, } +/// The prefix used by BABE for its VRF keys. +pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf"; + +macro_rules! babe_assert_eq { + ($a: expr, $b: expr) => { + { + let ref a = $a; + let ref b = $b; + if a != b { + error!( + target: "babe", + "Expected {:?} to equal {:?}, but they were not", + stringify!($a), + stringify!($b), + ); + assert_eq!(a, b); + } + } + }; +} + +type TmpDecode = ( + [u8; VRF_OUTPUT_LENGTH], + [u8; VRF_PROOF_LENGTH], + [u8; SIGNATURE_LENGTH], + [u8; PUBLIC_KEY_LENGTH], + u64, +); + impl Encode for BabeSeal { fn encode(&self) -> Vec { - parity_codec::Encode::encode(&( + let tmp: TmpDecode = ( + *self.vrf_output.as_bytes(), self.proof.to_bytes(), self.signature.signature.0, self.signature.signer.0, self.slot_num, - )) + ); + let encoded = parity_codec::Encode::encode(&tmp); + if cfg!(any(test, debug_assertions)) { + debug!(target: "babe", "Checking if encoding was correct"); + let decoded_version = Self::decode(&mut &encoded[..]) + .expect("we just encoded this ourselves, so it is correct; qed"); + babe_assert_eq!(decoded_version.proof, self.proof); + babe_assert_eq!(decoded_version.vrf_output, self.vrf_output); + babe_assert_eq!(decoded_version.signature.signature, self.signature.signature); + babe_assert_eq!(decoded_version.signature.signer, self.signature.signer); + babe_assert_eq!(decoded_version.slot_num, self.slot_num); + debug!(target: "babe", "Encoding was correct") + } + encoded } } impl Decode for BabeSeal { fn decode(i: &mut R) -> Option { - let (public_key, proof, sig, slot_num): ( - [u8; PUBLIC_KEY_LENGTH], - [u8; VRF_PROOF_LENGTH], - [u8; SIGNATURE_LENGTH], - u64, - ) = Decode::decode(i)?; + let (output, proof, sig, public_key, slot_num): TmpDecode = Decode::decode(i)?; Some(BabeSeal { proof: VRFProof::from_bytes(&proof).ok()?, + vrf_output: VRFOutput::from_bytes(&output).ok()?, signature: LocalizedSignature { signature: Signature(sig), signer: Public(public_key), @@ -75,19 +158,59 @@ impl Decode for BabeSeal { } } +/// A slot duration. Create with `get_or_compute`. +// FIXME: Once Rust has higher-kinded types, the duplication between this +// and `super::aura::Config` can be eliminated. +// https://github.com/paritytech/substrate/issues/2434 +pub struct Config(slots::SlotDuration); + +impl Config { + /// Either fetch the slot duration from disk or compute it from the genesis + /// state. + pub fn get_or_compute(client: &C) -> CResult + where + C: AuxStore, C: ProvideRuntimeApi, C::Api: BabeApi, + { + trace!(target: "babe", "Getting slot duration"); + match slots::SlotDuration::get_or_compute(client, |a, b| a.startup_data(b)).map(Self) { + Ok(s) => Ok(s), + Err(s) => { + warn!(target: "babe", "Failed to get slot duration"); + Err(s) + } + } + } + + /// Get the slot duration in milliseconds. + pub fn get(&self) -> u64 { + self.0.slot_duration + } + + /// Retrieve the threshold for BABE + pub fn threshold(&self) -> u64 { + self.0.threshold + } +} + +fn inherent_to_common_error(err: RuntimeString) -> consensus_common::Error { + consensus_common::ErrorKind::InherentData(err.into()).into() +} + /// A digest item which is usable with BABE consensus. pub trait CompatibleDigestItem: Sized { - /// Construct a digest item which contains a slot number and a signature on the - /// hash. + /// Construct a digest item which contains a slot number and a signature + /// on the hash. fn babe_seal(signature: BabeSeal) -> Self; /// If this item is an Babe seal, return the slot number and signature. fn as_babe_seal(&self) -> Option; } -impl CompatibleDigestItem for generic::DigestItem { - /// Construct a digest item which is aaAASSAAAAAASDC a slot number and a signature on the - /// hash. +impl CompatibleDigestItem for generic::DigestItem + where T: Debug, Hash: Debug +{ + /// Construct a digest item which contains a slot number and a signature + /// on the hash. fn babe_seal(signature: BabeSeal) -> Self { generic::DigestItem::Consensus(BABE_ENGINE_ID, signature.encode()) } @@ -95,8 +218,881 @@ impl CompatibleDigestItem for generic::DigestItem { /// If this item is an BABE seal, return the slot number and signature. fn as_babe_seal(&self) -> Option { match self { - generic::DigestItem::Consensus(BABE_ENGINE_ID, seal) => Decode::decode(&mut &seal[..]), - _ => None, + generic::DigestItem::Consensus(BABE_ENGINE_ID, seal) => { + match Decode::decode(&mut &seal[..]) { + s @ Some(_) => s, + s @ None => { + info!(target: "babe", "Failed to decode {:?}", seal); + s + } + } + } + _ => { + info!(target: "babe", "Invalid consensus: {:?}!", self); + None + } } } } + +struct BabeSlotCompatible; + +impl SlotCompatible for BabeSlotCompatible { + fn extract_timestamp_and_slot( + data: &InherentData + ) -> Result<(TimestampInherent, u64), 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(slots::inherent_to_common_error) + } +} + +/// Parameters for BABE. +pub struct BabeParams { + + /// The configuration for BABE. Includes the slot duration, threshold, and + /// other parameters. + pub config: Config, + + /// The key of the node we are running on. + pub local_key: Arc, + + /// The client to use + pub client: Arc, + + /// A block importer + pub block_import: Arc, + + /// The environment + pub env: Arc, + + /// A sync oracle + pub sync_oracle: SO, + + /// Exit callback. + pub on_exit: OnExit, + + /// Providers for inherent data. + pub inherent_data_providers: InherentDataProviders, + + /// Force authoring of blocks even if we are offline + pub force_authoring: bool, +} + +/// Start the babe worker. The returned future should be run in a tokio runtime. +pub fn start_babe(BabeParams { + config, + local_key, + client, + block_import, + env, + sync_oracle, + on_exit, + inherent_data_providers, + force_authoring, +}: BabeParams) -> Result< + impl Future, + consensus_common::Error, +> where + B: Block, + C: ChainHead + ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, + E: Environment, + E::Proposer: Proposer, + <>::Create as IntoFuture>::Future: Send + 'static, + I: BlockImport + Send + Sync + 'static, + SO: SyncOracle + Send + Sync + Clone, + DigestItemFor: CompatibleDigestItem + DigestItem, + Error: ::std::error::Error + Send + From<::consensus_common::Error> + From + 'static, + OnExit: Future, +{ + let worker = BabeWorker { + client: client.clone(), + block_import, + env, + local_key, + inherent_data_providers: inherent_data_providers.clone(), + sync_oracle: sync_oracle.clone(), + force_authoring, + threshold: config.threshold(), + }; + slots::start_slot_worker::<_, _, _, _, _, BabeSlotCompatible, _>( + config.0, + client, + Arc::new(worker), + sync_oracle, + on_exit, + inherent_data_providers + ) +} + +struct BabeWorker { + client: Arc, + block_import: Arc, + env: Arc, + local_key: Arc, + sync_oracle: SO, + inherent_data_providers: InherentDataProviders, + force_authoring: bool, + threshold: u64, +} + +impl SlotWorker for BabeWorker where + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, + E: Environment, + E::Proposer: Proposer, + <>::Create as IntoFuture>::Future: Send + 'static, + I: BlockImport + Send + Sync + 'static, + SO: SyncOracle + Send + Clone, + DigestItemFor: CompatibleDigestItem + DigestItem, + Error: std::error::Error + Send + From<::consensus_common::Error> + From + 'static, +{ + type OnSlot = Box + Send>; + + fn on_start( + &self, + slot_duration: u64 + ) -> Result<(), consensus_common::Error> { + register_babe_inherent_data_provider(&self.inherent_data_providers, slot_duration) + } + + fn on_slot( + &self, + chain_head: B::Header, + slot_info: SlotInfo, + ) -> Self::OnSlot { + let pair = self.local_key.clone(); + let ref client = self.client; + let block_import = self.block_import.clone(); + let ref env = self.env; + + let (timestamp, slot_num, slot_duration) = + (slot_info.timestamp, slot_info.number, slot_info.duration); + + let authorities = match authorities(client.as_ref(), &BlockId::Hash(chain_head.hash())) { + Ok(authorities) => authorities, + Err(e) => { + error!( + target: "babe", + "Unable to fetch authorities at block {:?}: {:?}", + chain_head.hash(), + e + ); + telemetry!(CONSENSUS_WARN; "babe.unable_fetching_authorities"; + "slot" => ?chain_head.hash(), "err" => ?e + ); + return Box::new(future::ok(())); + } + }; + + if !self.force_authoring && self.sync_oracle.is_offline() && authorities.len() > 1 { + debug!(target: "babe", "Skipping proposal slot. Waiting for the network."); + telemetry!(CONSENSUS_DEBUG; "babe.skipping_proposal_slot"; + "authorities_len" => authorities.len() + ); + return Box::new(future::ok(())); + } + + // FIXME replace the dummy empty slices with real data + // https://github.com/paritytech/substrate/issues/2435 + // https://github.com/paritytech/substrate/issues/2436 + let authoring_result = if let Some((inout, proof, _batchable_proof)) = claim_slot( + &[0u8; 0], + slot_info.number, + &[0u8; 0], + 0, + &authorities, + &pair, + self.threshold, + ) { + debug!( + target: "babe", "Starting authorship at slot {}; timestamp = {}", + slot_num, + timestamp, + ); + telemetry!(CONSENSUS_DEBUG; "babe.starting_authorship"; + "slot_num" => slot_num, "timestamp" => timestamp + ); + + // we are the slot author. make a block and sign it. + let proposer = match env.init(&chain_head, &authorities) { + Ok(p) => p, + Err(e) => { + warn!(target: "babe", "Unable to author block in slot {:?}: {:?}", slot_num, e); + telemetry!(CONSENSUS_WARN; "babe.unable_authoring_block"; + "slot" => slot_num, "err" => ?e + ); + return Box::new(future::ok(())) + } + }; + + let remaining_duration = slot_info.remaining_duration(); + // deadline our production to approx. the end of the + // slot + (Timeout::new( + proposer.propose( + slot_info.inherent_data, + remaining_duration, + ).into_future(), + remaining_duration, + ), + inout.to_output(), + proof) + } else { + return Box::new(future::ok(())); + }; + + let (proposal_work, vrf_output, proof) = authoring_result; + + Box::new( + proposal_work + .map(move |b| { + // minor hack since we don't have access to the timestamp + // that is actually set by the proposer. + let slot_after_building = slot_now(slot_duration); + if slot_after_building != Some(slot_num) { + info!( + target: "babe", + "Discarding proposal for slot {}; block production took too long", + slot_num + ); + telemetry!(CONSENSUS_INFO; "babe.discarding_proposal_took_too_long"; + "slot" => slot_num + ); + return + } + + let (header, body) = b.deconstruct(); + let header_num = header.number().clone(); + let pre_hash = header.hash(); + let parent_hash = header.parent_hash().clone(); + + // sign the pre-sealed hash of the block and then + // add it to a digest item. + let to_sign = (slot_num, pre_hash, proof.to_bytes()).encode(); + let signature = pair.sign(&to_sign[..]); + let item = as CompatibleDigestItem>::babe_seal(BabeSeal { + proof, + signature: LocalizedSignature { + signature, + signer: pair.public(), + }, + slot_num, + vrf_output, + }); + + let import_block: ImportBlock = ImportBlock { + origin: BlockOrigin::Own, + header, + justification: None, + post_digests: vec![item], + body: Some(body), + finalized: false, + auxiliary: Vec::new(), + fork_choice: ForkChoiceStrategy::LongestChain, + }; + + info!(target: "babe", + "Pre-sealed block for proposal at {}. Hash now {:?}, previously {:?}.", + header_num, + import_block.post_header().hash(), + pre_hash + ); + telemetry!(CONSENSUS_INFO; "babe.pre_sealed_block"; + "header_num" => ?header_num, + "hash_now" => ?import_block.post_header().hash(), + "hash_previously" => ?pre_hash + ); + + if let Err(e) = block_import.import_block(import_block, Default::default()) { + warn!(target: "babe", "Error with block built on {:?}: {:?}", + parent_hash, e); + telemetry!(CONSENSUS_WARN; "babe.err_with_block_built_on"; + "hash" => ?parent_hash, "err" => ?e + ); + } + }) + .map_err(|e| { + warn!("Client import failed: {:?}", e); + consensus_common::ErrorKind::ClientImport(format!("{:?}", e)).into() + }) + ) + } +} + +/// check a header has been signed by the right key. If the slot is too far in +/// the future, an error will be returned. If successful, returns the pre-header +/// and the digest item containing the seal. +/// +/// This digest item will always return `Some` when used with `as_babe_seal`. +// +// FIXME #1018 needs misbehavior types +#[forbid(warnings)] +fn check_header( + slot_now: u64, + mut header: B::Header, + hash: B::Hash, + authorities: &[Public], + threshold: u64, +) -> Result>, String> + where DigestItemFor: CompatibleDigestItem, +{ + trace!(target: "babe", "Checking header"); + let digest_item = match header.digest_mut().pop() { + Some(x) => x, + None => return Err(format!("Header {:?} is unsealed", hash)), + }; + + let BabeSeal { + slot_num, + signature: LocalizedSignature {signer, signature }, + proof, + vrf_output, + } = digest_item.as_babe_seal().ok_or_else(|| { + debug!(target: "babe", "Header {:?} is unsealed", hash); + format!("Header {:?} is unsealed", hash) + })?; + + if slot_num > slot_now { + header.digest_mut().push(digest_item); + Ok(CheckedHeader::Deferred(header, slot_num)) + } else if !authorities.contains(&signer) { + debug!(target: "babe", "Slot Author not found"); + Err("Slot Author not found".to_string()) + } else { + let pre_hash = header.hash(); + let to_sign = (slot_num, pre_hash, proof.to_bytes()).encode(); + + if sr25519::Pair::verify(&signature, &to_sign[..], &signer) { + let (inout, _batchable_proof) = { + let transcript = make_transcript( + Default::default(), + slot_num, + Default::default(), + 0, + ); + schnorrkel::PublicKey::from_bytes(signer.as_slice()).and_then(|p| { + p.vrf_verify(transcript, &vrf_output, &proof) + }).map_err(|s| { + debug!(target: "babe", "VRF verification failed: {:?}", s); + format!("VRF verification failed") + })? + }; + if check(&inout, threshold) { + Ok(CheckedHeader::Checked(header, digest_item)) + } else { + debug!(target: "babe", "VRF verification failed: threshold {} exceeded", threshold); + Err(format!("Validator {:?} made seal when it wasn’t its turn", signer)) + } + } else { + debug!(target: "babe", "Bad signature on {:?}", hash); + Err(format!("Bad signature on {:?}", hash)) + } + } +} + +/// A verifier for Babe blocks. +pub struct BabeVerifier { + client: Arc, + extra: E, + inherent_data_providers: inherents::InherentDataProviders, + threshold: u64, +} + +impl BabeVerifier { + fn check_inherents( + &self, + block: B, + block_id: BlockId, + inherent_data: InherentData, + ) -> Result<(), String> + where C: ProvideRuntimeApi, C::Api: BlockBuilderApi + { + let inherent_res = self.client.runtime_api().check_inherents( + &block_id, + block, + inherent_data, + ).map_err(|e| format!("{:?}", e))?; + + if !inherent_res.ok() { + inherent_res + .into_errors() + .try_for_each(|(i, e)| Err(self.inherent_data_providers.error_to_string(&i, &e))) + } else { + Ok(()) + } + } +} + +/// No-op extra verification. +#[derive(Debug, Clone, Copy)] +pub struct NothingExtra; + +impl ExtraVerification for NothingExtra { + type Verified = Result<(), String>; + + fn verify(&self, _: &B::Header, _: Option<&[B::Extrinsic]>) -> Self::Verified { + Ok(()) + } +} + +impl Verifier for BabeVerifier where + C: ProvideRuntimeApi + Send + Sync, + C::Api: BlockBuilderApi, + DigestItemFor: CompatibleDigestItem + DigestItem, + E: ExtraVerification, + Self: Authorities, +{ + fn verify( + &self, + origin: BlockOrigin, + header: B::Header, + justification: Option, + mut body: Option>, + ) -> Result<(ImportBlock, Option>), String> { + trace!( + target: "babe", + "Verifying origin: {:?} header: {:?} justification: {:?} body: {:?}", + origin, + header, + justification, + body, + ); + let mut inherent_data = self + .inherent_data_providers + .create_inherent_data() + .map_err(String::from)?; + let (_, slot_now) = BabeSlotCompatible::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(); + let authorities = self.authorities(&BlockId::Hash(parent_hash)) + .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; + + let extra_verification = self.extra.verify( + &header, + body.as_ref().map(|x| &x[..]), + ); + + // we add one to allow for some small drift. + // FIXME #1019 in the future, alter this queue to allow deferring of + // headers + let checked_header = check_header::( + slot_now + 1, + header, + hash, + &authorities[..], + self.threshold, + )?; + match checked_header { + CheckedHeader::Checked(pre_header, seal) => { + let BabeSeal { slot_num, .. } = seal.as_babe_seal() + .expect("check_header always returns a seal digest item; qed"); + + // if the body is passed through, we need to use the runtime + // to check that the internally-set timestamp in the inherents + // actually matches the slot set in the seal. + if let Some(inner_body) = body.take() { + inherent_data.babe_replace_inherent_data(slot_num); + let block = B::new(pre_header.clone(), inner_body); + + self.check_inherents( + block.clone(), + BlockId::Hash(parent_hash), + inherent_data, + )?; + + let (_, inner_body) = block.deconstruct(); + body = Some(inner_body); + } + + trace!(target: "babe", "Checked {:?}; importing.", pre_header); + telemetry!( + CONSENSUS_TRACE; + "babe.checked_and_importing"; + "pre_header" => ?pre_header); + + extra_verification.into_future().wait()?; + + let import_block = ImportBlock { + origin, + header: pre_header, + post_digests: vec![seal], + body, + finalized: false, + justification, + auxiliary: Vec::new(), + fork_choice: ForkChoiceStrategy::LongestChain, + }; + + // FIXME #1019 extract authorities + Ok((import_block, None)) + } + CheckedHeader::Deferred(a, b) => { + debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); + telemetry!(CONSENSUS_DEBUG; "babe.header_too_far_in_future"; + "hash" => ?hash, "a" => ?a, "b" => ?b + ); + Err(format!("Header {:?} rejected: too far in the future", hash)) + } + } + } +} + +impl Authorities for BabeVerifier where + B: Block, + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, +{ + type Error = ConsensusError; + + fn authorities(&self, at: &BlockId) -> Result>, Self::Error> { + authorities(self.client.as_ref(), at) + } +} + +fn authorities(client: &C, at: &BlockId) -> Result< + Vec>, + ConsensusError, +> where + B: Block, + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, +{ + client + .cache() + .and_then(|cache| cache.get_at(&well_known_cache_keys::AUTHORITIES, at) + .and_then(|v| Decode::decode(&mut &v[..]))) + .or_else(|| { + if client.runtime_api().has_api::>(at).unwrap_or(false) { + AuthoritiesApi::authorities(&*client.runtime_api(), at).ok() + } else { + panic!("We don’t support deprecated code with new consensus algorithms, \ + therefore this is unreachable; qed") + } + }).ok_or_else(|| consensus_common::ErrorKind::InvalidAuthoritiesSet.into()) +} + +/// The BABE import queue type. +pub type BabeImportQueue = BasicQueue; + +/// Register the babe inherent data provider, if not registered already. +fn register_babe_inherent_data_provider( + inherent_data_providers: &InherentDataProviders, + slot_duration: u64, +) -> Result<(), consensus_common::Error> { + debug!(target: "babe", "Registering"); + if !inherent_data_providers.has_provider(&srml_babe::INHERENT_IDENTIFIER) { + inherent_data_providers + .register_provider(srml_babe::InherentDataProvider::new(slot_duration)) + .map_err(inherent_to_common_error) + } else { + Ok(()) + } +} + +fn get_keypair(q: &sr25519::Pair) -> &Keypair { + q.as_ref() +} + +fn make_transcript( + randomness: &[u8], + slot_number: u64, + genesis_hash: &[u8], + epoch: u64, +) -> Transcript { + let mut transcript = Transcript::new(&BABE_ENGINE_ID); + transcript.commit_bytes(b"slot number", &slot_number.to_le_bytes()); + transcript.commit_bytes(b"genesis block hash", genesis_hash); + transcript.commit_bytes(b"current epoch", &epoch.to_le_bytes()); + transcript.commit_bytes(b"chain randomness", randomness); + transcript +} + +fn check(inout: &VRFInOut, threshold: u64) -> bool { + u64::from_le_bytes(inout.make_bytes::<[u8; 8]>(BABE_VRF_PREFIX)) < threshold +} + +/// Claim a slot if it is our turn. Returns `None` if it is not our turn. +/// +/// This hashes the slot number, epoch, genesis hash, and chain randomness into +/// the VRF. If the VRF produces a value less than `threshold`, it is our turn, +/// so it returns `Some(_)`. Otherwise, it returns `None`. +fn claim_slot( + randomness: &[u8], + slot_number: u64, + genesis_hash: &[u8], + epoch: u64, + authorities: &[sr25519::Public], + key: &sr25519::Pair, + threshold: u64, +) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> { + if !authorities.contains(&key.public()) { return None } + let transcript = make_transcript( + randomness, + slot_number, + genesis_hash, + epoch, + ); + + // Compute the threshold we will use. + // + // We already checked that authorities contains `key.public()`, so it can’t + // 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)) +} + +#[cfg(test)] +#[allow(dead_code, unused_imports)] +mod tests { + use super::*; + + use consensus_common::NoNetwork as DummyOracle; + use network::test::*; + use network::test::{Block as TestBlock, PeersClient}; + use runtime_primitives::traits::Block as BlockT; + use network::config::ProtocolConfig; + use parking_lot::Mutex; + use tokio::runtime::current_thread; + use keyring::sr25519::Keyring; + use client::BlockchainEvents; + use test_client; + use futures::stream::Stream; + use log::debug; + use std::time::Duration; + + type Error = client::error::Error; + + type TestClient = client::Client< + test_client::Backend, + test_client::Executor, + TestBlock, + test_client::runtime::RuntimeApi, + >; + + struct DummyFactory(Arc); + struct DummyProposer(u64, Arc); + + impl Environment for DummyFactory { + type Proposer = DummyProposer; + type Error = Error; + + fn init(&self, parent_header: &::Header, _authorities: &[Public]) + -> Result + { + Ok(DummyProposer(parent_header.number + 1, self.0.clone())) + } + } + + impl Proposer for DummyProposer { + type Error = Error; + type Create = Result; + + fn propose(&self, _: InherentData, _: Duration) -> Result { + self.1.new_block().unwrap().bake().map_err(|e| e.into()) + } + } + + const SLOT_DURATION: u64 = 1; + const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); + + pub struct BabeTestNet { + peers: Vec>>, + started: bool, + } + + impl TestNetFactory for BabeTestNet { + type Specialization = DummySpecialization; + type Verifier = BabeVerifier; + type PeerData = (); + + /// Create new test network with peers and given config. + fn from_config(_config: &ProtocolConfig) -> Self { + debug!(target: "babe", "Creating test network from config"); + BabeTestNet { + peers: Vec::new(), + started: false, + } + } + + fn make_verifier(&self, client: Arc, _cfg: &ProtocolConfig) + -> Arc + { + trace!(target: "babe", "Creating a verifier"); + let config = Config::get_or_compute(&*client) + .expect("slot duration available"); + let inherent_data_providers = InherentDataProviders::new(); + register_babe_inherent_data_provider( + &inherent_data_providers, + config.get() + ).expect("Registers babe inherent data provider"); + trace!(target: "babe", "Provider registered"); + + assert_eq!(config.get(), SLOT_DURATION); + Arc::new(BabeVerifier { + client, + extra: NothingExtra, + inherent_data_providers, + threshold: config.threshold(), + }) + } + + fn peer(&self, i: usize) -> &Peer { + trace!(target: "babe", "Retreiving a peer"); + &self.peers[i] + } + + fn peers(&self) -> &Vec>> { + trace!(target: "babe", "Retreiving peers"); + &self.peers + } + + fn mut_peers>>)>( + &mut self, + closure: F, + ) { + closure(&mut self.peers); + } + + fn started(&self) -> bool { + self.started + } + + fn set_started(&mut self, new: bool) { + self.started = new; + } + } + + #[test] + fn can_serialize_block() { + drop(env_logger::try_init()); + assert!(BabeSeal::decode(&mut &b""[..]).is_none()); + } + + #[test] + fn authoring_blocks() { + drop(env_logger::try_init()); + debug!(target: "babe", "checkpoint 1"); + let mut net = BabeTestNet::new(3); + debug!(target: "babe", "checkpoint 2"); + + net.start(); + debug!(target: "babe", "checkpoint 3"); + + let peers = &[ + (0, Keyring::Alice), + (1, Keyring::Bob), + (2, Keyring::Charlie), + ]; + + let net = Arc::new(Mutex::new(net)); + let mut import_notifications = Vec::new(); + debug!(target: "babe", "checkpoint 4"); + let mut runtime = current_thread::Runtime::new().unwrap(); + for (peer_id, key) in peers { + let client = net.lock().peer(*peer_id).client().clone(); + let environ = Arc::new(DummyFactory(client.clone())); + import_notifications.push( + client.import_notification_stream() + .take_while(|n| Ok(!(n.origin != BlockOrigin::Own && n.header.number() < &5))) + .for_each(move |_| Ok(())) + ); + + let config = Config::get_or_compute(&*client) + .expect("slot duration available"); + + let inherent_data_providers = InherentDataProviders::new(); + register_babe_inherent_data_provider( + &inherent_data_providers, config.get() + ).expect("Registers babe inherent data provider"); + + let babe = start_babe(BabeParams { + config, + local_key: Arc::new(key.clone().into()), + block_import: client.clone(), + client, + env: environ.clone(), + sync_oracle: DummyOracle, + on_exit: futures::empty(), + inherent_data_providers, + force_authoring: false, + }).expect("Starts babe"); + + runtime.spawn(babe); + } + debug!(target: "babe", "checkpoint 5"); + + // wait for all finalized on each. + let wait_for = ::futures::future::join_all(import_notifications) + .map(drop) + .map_err(drop); + + let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) + .for_each(move |_| { + net.lock().send_import_notifications(); + net.lock().sync_without_disconnects(); + Ok(()) + }) + .map(drop) + .map_err(drop); + + runtime.block_on(wait_for.select(drive_to_completion).map_err(drop)).unwrap(); + } + + #[test] + #[allow(deprecated)] + #[should_panic] + fn old_seals_rejected() { + drop(env_logger::try_init()); + generic::DigestItem::::Seal(0, Signature([0; 64])).as_babe_seal().unwrap(); + } + + #[test] + fn wrong_number_rejected() { + drop(env_logger::try_init()); + let bad_seal = generic::DigestItem::::Consensus([0; 4], Signature([0; 64]).encode()); + assert!(bad_seal.as_babe_seal().is_none()) + } + + #[test] + #[should_panic] + fn bad_seal_rejected() { + drop(env_logger::try_init()); + let bad_seal = generic::DigestItem::::Consensus(BABE_ENGINE_ID, Signature([0; 64]).encode()); + bad_seal.as_babe_seal().expect("we should not decode this successfully"); + } + + #[test] + fn can_author_block() { + drop(env_logger::try_init()); + let randomness = &[]; + let pair = sr25519::Pair::generate(); + let mut i = 0; + loop { + match claim_slot(randomness, i, &[], 0, &[pair.public()], &pair, u64::MAX / 10) { + None => i += 1, + Some(s) => { + debug!(target: "babe", "Authored block {:?}", s); + break + } + } + } + } + + #[test] + fn authorities_call_works() { + drop(env_logger::try_init()); + let client = test_client::new(); + + assert_eq!(client.info().unwrap().chain.best_number, 0); + assert_eq!(authorities(&client, &BlockId::Number(0)).unwrap(), vec![ + Keyring::Alice.into(), + Keyring::Bob.into(), + Keyring::Charlie.into() + ]); + } +} diff --git a/substrate/core/consensus/common/src/import_queue.rs b/substrate/core/consensus/common/src/import_queue.rs index 160538966f..209e27e5e2 100644 --- a/substrate/core/consensus/common/src/import_queue.rs +++ b/substrate/core/consensus/common/src/import_queue.rs @@ -17,12 +17,13 @@ //! Import Queue primitive: something which can verify and import blocks. //! //! This serves as an intermediate and abstracted step between synchronization -//! and import. Each mode of consensus will have its own requirements for block verification. -//! Some algorithms can verify in parallel, while others only sequentially. +//! and import. Each mode of consensus will have its own requirements for block +//! verification. Some algorithms can verify in parallel, while others only +//! sequentially. //! -//! The `ImportQueue` trait allows such verification strategies to be instantiated. -//! The `BasicQueue` and `BasicVerifier` traits allow serial queues to be -//! instantiated simply. +//! The `ImportQueue` trait allows such verification strategies to be +//! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial +//! queues to be instantiated simply. use crate::block_import::{ BlockImport, BlockOrigin, ImportBlock, ImportedAux, ImportResult, JustificationImport, @@ -106,8 +107,8 @@ impl Clone for Box> { } } -/// Interface to a basic block import queue that is importing blocks sequentially in a separate thread, -/// with pluggable verification. +/// Interface to a basic block import queue that is importing blocks +/// sequentially in a separate thread, with pluggable verification. #[derive(Clone)] pub struct BasicQueue { sender: Sender>, @@ -120,20 +121,24 @@ impl ImportQueueClone for BasicQueue { } /// "BasicQueue" is a wrapper around a channel sender to the "BlockImporter". -/// "BasicQueue" itself does not keep any state or do any importing work, and can therefore be send to other threads. +/// "BasicQueue" itself does not keep any state or do any importing work, and +/// can therefore be send to other threads. /// -/// "BasicQueue" implements "ImportQueue" by sending messages to the "BlockImporter", which runs in it's own thread. +/// "BasicQueue" implements "ImportQueue" by sending messages to the +/// "BlockImporter", which runs in it's own thread. /// -/// The "BlockImporter" is responsible for handling incoming requests from the "BasicQueue", -/// some of these requests are handled by the "BlockImporter" itself, such as "is_importing" or "status", -/// and justifications are also imported by the "BlockImporter". +/// The "BlockImporter" is responsible for handling incoming requests from the +/// "BasicQueue". Some of these requests are handled by the "BlockImporter" +/// itself, such as "is_importing", "status", and justifications. /// -/// The "import block" work will be offloaded to a single "BlockImportWorker", running in another thread. -/// Offloading the work is done via a channel, -/// ensuring blocks in this implementation are imported sequentially and in order(as received by the "BlockImporter") +/// The "import block" work will be offloaded to a single "BlockImportWorker", +/// running in another thread. Offloading the work is done via a channel, +/// ensuring blocks in this implementation are imported sequentially and in +/// order (as received by the "BlockImporter"). /// -/// As long as the "BasicQueue" is not dropped, the "BlockImporter" will keep running. -/// The "BlockImporter" owns a sender to the "BlockImportWorker", ensuring that the worker is kept alive until that sender is dropped. +/// As long as the "BasicQueue" is not dropped, the "BlockImporter" will keep +/// running. The "BlockImporter" owns a sender to the "BlockImportWorker", +/// ensuring that the worker is kept alive until that sender is dropped. impl BasicQueue { /// Instantiate a new basic queue, with given verifier. pub fn new>( @@ -152,8 +157,8 @@ impl BasicQueue { /// Send synchronization request to the block import channel. /// - /// The caller should wait for Link::synchronized() call to ensure that it has synchronized - /// with ImportQueue. + /// The caller should wait for Link::synchronized() call to ensure that it + /// has synchronized with ImportQueue. #[cfg(any(test, feature = "test-helpers"))] pub fn synchronize(&self) { self @@ -238,6 +243,7 @@ impl BlockImporter { worker_sender: Sender>, justification_import: Option>, ) -> Sender> { + trace!(target: "block_import", "Creating new Block Importer!"); let (sender, port) = channel::bounded(4); let _ = thread::Builder::new() .name("ImportQueue".into()) @@ -258,6 +264,7 @@ impl BlockImporter { } fn run(&mut self) -> bool { + trace!(target: "import_queue", "Running import queue"); let msg = select! { recv(self.port) -> msg => { match msg { @@ -297,6 +304,7 @@ impl BlockImporter { BlockImportMsg::Stop => return false, #[cfg(any(test, feature = "test-helpers"))] BlockImportMsg::Synchronize => { + trace!(target: "sync", "Received synchronization message"); self.worker_sender .send(BlockImportWorkerMsg::Synchronize) .expect("1. This is holding a sender to the worker, 2. the worker should not quit while a sender is still held; qed"); @@ -318,6 +326,7 @@ impl BlockImporter { BlockImportWorkerMsg::Imported(results) => (results), #[cfg(any(test, feature = "test-helpers"))] BlockImportWorkerMsg::Synchronize => { + trace!(target: "sync", "Synchronizing link"); link.synchronized(); return true; }, @@ -434,6 +443,7 @@ impl> BlockImportWorker { }, #[cfg(any(test, feature = "test-helpers"))] BlockImportWorkerMsg::Synchronize => { + trace!(target: "sync", "Sending sync message"); let _ = worker.result_sender.send(BlockImportWorkerMsg::Synchronize); }, _ => unreachable!("Import Worker does not receive the Imported message; qed"), diff --git a/substrate/core/consensus/common/src/lib.rs b/substrate/core/consensus/common/src/lib.rs index 134a34454e..7d846a32e3 100644 --- a/substrate/core/consensus/common/src/lib.rs +++ b/substrate/core/consensus/common/src/lib.rs @@ -118,6 +118,20 @@ impl SyncOracle for Arc { } } +/// Extra verification for blocks. +pub trait ExtraVerification: Send + Sync { + /// Future that resolves when the block is verified, or fails with error if + /// not. + type Verified: IntoFuture; + + /// Do additional verification for this block. + fn verify( + &self, + header: &B::Header, + body: Option<&[B::Extrinsic]>, + ) -> Self::Verified; +} + /// A list of all well known keys in the cache. pub mod well_known_cache_keys { /// The type representing cache keys. diff --git a/substrate/core/consensus/slots/src/lib.rs b/substrate/core/consensus/slots/src/lib.rs index 600df43abf..ee8004661d 100644 --- a/substrate/core/consensus/slots/src/lib.rs +++ b/substrate/core/consensus/slots/src/lib.rs @@ -24,45 +24,44 @@ mod slots; -pub use slots::{Slots, SlotInfo}; +pub use slots::{slot_now, SlotInfo, Slots}; +use client::ChainHead; +use codec::{Decode, Encode}; +use consensus_common::SyncOracle; +use futures::prelude::*; +use futures::{ + future::{self, Either}, + Future, IntoFuture, +}; +use inherents::{InherentData, InherentDataProviders}; +use log::{debug, error, info, warn}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::{ApiRef, Block, ProvideRuntimeApi}; +use std::fmt::Debug; +use std::ops::Deref; use std::sync::{mpsc, Arc}; use std::thread; -use std::fmt::Debug; -use futures::prelude::*; -use futures::{Future, IntoFuture, future::{self, Either}}; -use log::{warn, debug, info}; -use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{ProvideRuntimeApi, Block, ApiRef}; -use consensus_common::SyncOracle; -use inherents::{InherentData, InherentDataProviders}; -use client::ChainHead; -use codec::{Encode, Decode}; /// A worker that should be invoked at every new slot. pub trait SlotWorker { /// The type of the future that will be returned when a new slot is /// triggered. - type OnSlot: IntoFuture; + type OnSlot: IntoFuture; /// Called when the proposer starts. - fn on_start( - &self, - slot_duration: u64 - ) -> Result<(), consensus_common::Error>; + fn on_start(&self, slot_duration: u64) -> Result<(), consensus_common::Error>; /// 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; } /// Slot compatible inherent data. pub trait SlotCompatible { /// Extract timestamp and slot from inherent data. - fn extract_timestamp_and_slot(inherent: &InherentData) -> Result<(u64, u64), consensus_common::Error>; + fn extract_timestamp_and_slot( + inherent: &InherentData, + ) -> Result<(u64, u64), consensus_common::Error>; } /// Convert an inherent error to common error. @@ -79,13 +78,14 @@ pub fn start_slot_worker_thread( sync_oracle: SO, on_exit: OnExit, inherent_data_providers: InherentDataProviders, -) -> Result<(), consensus_common::Error> where +) -> Result<(), consensus_common::Error> +where B: Block + 'static, C: ChainHead + Send + Sync + 'static, W: SlotWorker + Send + Sync + 'static, SO: SyncOracle + Send + Clone + 'static, SC: SlotCompatible + 'static, - OnExit: Future + Send + 'static, + OnExit: Future + Send + 'static, T: SlotData + Send + Clone + 'static, { use tokio::runtime::current_thread::Runtime; @@ -96,7 +96,7 @@ pub fn start_slot_worker_thread( let mut runtime = match Runtime::new() { Ok(r) => r, Err(e) => { - warn!("Unable to start authorship: {:?}", e); + warn!(target: "slots", "Unable to start authorship: {:?}", e); return; } }; @@ -114,7 +114,7 @@ pub fn start_slot_worker_thread( .send(Ok(())) .expect("Receive is not dropped before receiving a result; qed"); slot_worker_future - }, + } Err(e) => { result_sender .send(Err(e)) @@ -126,7 +126,9 @@ pub fn start_slot_worker_thread( let _ = runtime.block_on(slot_worker_future); }); - result_recv.recv().expect("Slots start thread result sender dropped") + result_recv + .recv() + .expect("Slots start thread result sender dropped") } /// Start a new slot worker. @@ -137,13 +139,14 @@ pub fn start_slot_worker( sync_oracle: SO, on_exit: OnExit, inherent_data_providers: InherentDataProviders, -) -> Result, consensus_common::Error> where +) -> Result, consensus_common::Error> +where B: Block, C: ChainHead, W: SlotWorker, SO: SyncOracle + Send + Clone, SC: SlotCompatible, - OnExit: Future, + OnExit: Future, T: SlotData + Clone, { worker.on_start(slot_duration.slot_duration())?; @@ -174,15 +177,14 @@ pub fn start_slot_worker( Ok(x) => x, Err(e) => { warn!(target: "slots", "Unable to author block in slot {}. \ - no best block header: {:?}", slot_num, e); - return Either::B(future::ok(())) + no best block header: {:?}", slot_num, e); + return Either::B(future::ok(())); } }; - Either::A( - worker.on_slot(chain_head, slot_info).into_future() - .map_err(|e| debug!(target: "slots", "Encountered consensus error: {:?}", e)) - ) + Either::A(worker.on_slot(chain_head, slot_info).into_future().map_err( + |e| warn!(target: "slots", "Encountered consensus error: {:?}", e), + )) }) }; @@ -230,7 +232,9 @@ pub trait SlotData { } impl SlotData for u64 { - fn slot_duration(&self) -> u64 { *self } + fn slot_duration(&self) -> u64 { + *self + } const SLOT_KEY: &'static [u8] = b"aura_slot_duration"; } @@ -238,7 +242,25 @@ impl SlotData for u64 { /// A slot duration. Create with `get_or_compute`. // The internal member should stay private here. #[derive(Clone, Copy, Debug, Encode, Decode, Hash, PartialOrd, Ord, PartialEq, Eq)] -pub struct SlotDuration(T); +pub struct SlotDuration(T); + +impl Deref for SlotDuration { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } +} + +impl SlotData for SlotDuration { + /// Get the slot duration in milliseconds. + fn slot_duration(&self) -> u64 + where T: SlotData, + { + self.0.slot_duration() + } + + const SLOT_KEY: &'static [u8] = T::SLOT_KEY; +} impl SlotDuration { /// Either fetch the slot duration from disk or compute it from the @@ -255,23 +277,25 @@ impl SlotDuration { match client.get_aux(T::SLOT_KEY)? { Some(v) => ::decode(&mut &v[..]) .map(SlotDuration) - .ok_or_else(|| ::client::error::Error::Backend( - format!("slot duration kept in invalid format"), - ).into()), + .ok_or_else(|| { + ::client::error::Error::Backend({ + error!(target: "slots", "slot duration kept in invalid format"); + format!("slot duration kept in invalid format") + }) + .into() + }), None => { use runtime_primitives::traits::Zero; - let genesis_slot_duration = cb( - client.runtime_api(), - &BlockId::number(Zero::zero()))?; + let genesis_slot_duration = + cb(client.runtime_api(), &BlockId::number(Zero::zero()))?; info!( "Loaded block-time = {:?} seconds from genesis on first-launch", genesis_slot_duration ); - genesis_slot_duration.using_encoded(|s| { - client.insert_aux(&[(T::SLOT_KEY, &s[..])], &[]) - })?; + genesis_slot_duration + .using_encoded(|s| client.insert_aux(&[(T::SLOT_KEY, &s[..])], &[]))?; Ok(SlotDuration(genesis_slot_duration)) } @@ -282,11 +306,4 @@ impl SlotDuration { pub fn get(&self) -> T { self.0.clone() } - - /// Get the slot duration in milliseconds - pub fn slot_duration(&self) -> u64 - where T: SlotData - { - self.0.slot_duration() - } } diff --git a/substrate/core/consensus/slots/src/slots.rs b/substrate/core/consensus/slots/src/slots.rs index 9b665ce0d2..df21ae9b83 100644 --- a/substrate/core/consensus/slots/src/slots.rs +++ b/substrate/core/consensus/slots/src/slots.rs @@ -18,24 +18,34 @@ //! //! This is used instead of `tokio_timer::Interval` because it was unreliable. -use std::time::{Instant, Duration}; -use std::marker::PhantomData; -use tokio::timer::Delay; +use super::SlotCompatible; +use consensus_common::{Error, ErrorKind}; use futures::prelude::*; use futures::try_ready; +use inherents::{InherentData, InherentDataProviders}; use log::warn; -use inherents::{InherentDataProviders, InherentData}; -use consensus_common::{Error, ErrorKind}; -use crate::SlotCompatible; +use std::marker::PhantomData; +use std::time::{Duration, Instant}; +use tokio::timer::Delay; /// Returns current duration since unix epoch. pub fn duration_now() -> Option { use std::time::SystemTime; let now = SystemTime::now(); - now.duration_since(SystemTime::UNIX_EPOCH).map_err(|e| { - warn!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e); - }).ok() + now.duration_since(SystemTime::UNIX_EPOCH) + .map_err(|e| { + warn!( + "Current time {:?} is before unix epoch. Something is wrong: {:?}", + now, e + ); + }) + .ok() +} + +/// Get the slot for now. +pub fn slot_now(slot_duration: u64) -> Option { + duration_now().map(|s| s.as_secs() / slot_duration) } /// Returns the duration until the next slot, based on current duration since @@ -113,34 +123,35 @@ impl Stream for Slots { }; if let Some(ref mut inner_delay) = self.inner_delay { - try_ready!(inner_delay.poll().map_err(|e| Error::from(ErrorKind::FaultyTimer(e)))); + try_ready!(inner_delay + .poll() + .map_err(|e| Error::from(ErrorKind::FaultyTimer(e)))); } // timeout has fired. - let inherent_data = self.inherent_data_providers.create_inherent_data() + let inherent_data = self + .inherent_data_providers + .create_inherent_data() .map_err(crate::inherent_to_common_error)?; let (timestamp, slot_num) = SC::extract_timestamp_and_slot(&inherent_data)?; // reschedule delay for next slot. - let ends_at = Instant::now() + time_until_next(Duration::from_secs(timestamp), slot_duration); + 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, - }) - ) - ) + 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() diff --git a/substrate/core/finality-grandpa/src/import.rs b/substrate/core/finality-grandpa/src/import.rs index 34e266ddd3..c3bd99247a 100644 --- a/substrate/core/finality-grandpa/src/import.rs +++ b/substrate/core/finality-grandpa/src/import.rs @@ -33,10 +33,10 @@ use fg_primitives::GrandpaApi; use runtime_primitives::Justification; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{ - Block as BlockT, DigestFor, DigestItemFor, DigestItem, + Block as BlockT, DigestFor, Header as HeaderT, NumberFor, ProvideRuntimeApi, }; -use substrate_primitives::{H256, ed25519, Blake2Hasher}; +use substrate_primitives::{H256, Blake2Hasher}; use crate::{Error, CommandOrError, NewAuthoritySet, VoterCommand}; use crate::authorities::{AuthoritySet, SharedAuthoritySet, DelayKind, PendingChange}; @@ -44,8 +44,6 @@ use crate::consensus_changes::SharedConsensusChanges; use crate::environment::{finalize_block, is_descendent_of}; use crate::justification::GrandpaJustification; -use ed25519::Public as AuthorityId; - /// A block-import handler for GRANDPA. /// /// This scans each imported block for signals of changing authority set. @@ -69,7 +67,6 @@ impl, RA, PRA> JustificationImport B: Backend + 'static, E: CallExecutor + 'static + Clone + Send + Sync, DigestFor: Encode, - DigestItemFor: DigestItem, RA: Send + Sync, PRA: ProvideRuntimeApi, PRA::Api: GrandpaApi, @@ -163,7 +160,6 @@ impl, RA, PRA> GrandpaBlockImport + 'static, E: CallExecutor + 'static + Clone + Send + Sync, DigestFor: Encode, - DigestItemFor: DigestItem, RA: Send + Sync, PRA: ProvideRuntimeApi, PRA::Api: GrandpaApi, @@ -381,7 +377,6 @@ impl, RA, PRA> BlockImport B: Backend + 'static, E: CallExecutor + 'static + Clone + Send + Sync, DigestFor: Encode, - DigestItemFor: DigestItem, RA: Send + Sync, PRA: ProvideRuntimeApi, PRA::Api: GrandpaApi, diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs index 5a2b084160..1a20e19833 100644 --- a/substrate/core/finality-grandpa/src/lib.rs +++ b/substrate/core/finality-grandpa/src/lib.rs @@ -51,7 +51,7 @@ //! number (this is num(signal) + N). When finalizing a block, we either apply //! or prune any signaled changes based on whether the signaling block is //! included in the newly-finalized chain. - +#![forbid(warnings)] use futures::prelude::*; use log::{debug, info, warn}; use futures::sync::mpsc; @@ -62,7 +62,7 @@ use client::{ use client::blockchain::HeaderBackend; use parity_codec::Encode; use runtime_primitives::traits::{ - NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi, DigestItemFor, DigestItem, + NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi, }; use fg_primitives::GrandpaApi; use inherents::InherentDataProviders; @@ -362,7 +362,6 @@ fn global_communication, B, E, N, RA>( N: Network, RA: Send + Sync, NumberFor: BlockNumberOps, - DigestItemFor: DigestItem, { let is_voter = local_key @@ -463,7 +462,6 @@ pub fn run_grandpa_voter, N, RA, X>( N::In: Send + 'static, NumberFor: BlockNumberOps, DigestFor: Encode, - DigestItemFor: DigestItem, RA: Send + Sync + 'static, X: Future + Clone + Send + 'static, { @@ -716,7 +714,6 @@ pub fn run_grandpa, N, RA, X>( N::In: Send + 'static, NumberFor: BlockNumberOps, DigestFor: Encode, - DigestItemFor: DigestItem, RA: Send + Sync + 'static, X: Future + Clone + Send + 'static, { diff --git a/substrate/core/finality-grandpa/src/observer.rs b/substrate/core/finality-grandpa/src/observer.rs index cce68a64d4..1e81e084e7 100644 --- a/substrate/core/finality-grandpa/src/observer.rs +++ b/substrate/core/finality-grandpa/src/observer.rs @@ -25,9 +25,8 @@ use grandpa::{ use log::{debug, info, warn}; use client::{CallExecutor, Client, backend::Backend}; -use ed25519::Public as AuthorityId; -use runtime_primitives::traits::{NumberFor, Block as BlockT, DigestItemFor, DigestItem}; -use substrate_primitives::{ed25519, H256, Blake2Hasher}; +use runtime_primitives::traits::{NumberFor, Block as BlockT}; +use substrate_primitives::{ed25519::Public as AuthorityId, H256, Blake2Hasher}; use crate::{ AuthoritySignature, global_communication, CommandOrError, Config, environment, @@ -155,7 +154,6 @@ pub fn run_grandpa_observer, N, RA>( N: Network + Send + Sync + 'static, N::In: Send + 'static, NumberFor: BlockNumberOps, - DigestItemFor: DigestItem, RA: Send + Sync + 'static, { let LinkHalf { diff --git a/substrate/core/finality-grandpa/src/tests.rs b/substrate/core/finality-grandpa/src/tests.rs index 3607ae04d8..a631ec0c07 100644 --- a/substrate/core/finality-grandpa/src/tests.rs +++ b/substrate/core/finality-grandpa/src/tests.rs @@ -23,7 +23,7 @@ use network::config::{ProtocolConfig, Roles}; use network::consensus_gossip as network_gossip; use parking_lot::Mutex; use tokio::runtime::current_thread; -use keyring::AuthorityKeyring; +use keyring::ed25519::{Keyring as AuthorityKeyring}; use client::{ BlockchainEvents, error::Result, blockchain::Backend as BlockchainBackend, @@ -36,7 +36,7 @@ use std::collections::{HashMap, HashSet}; use std::result; use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi, Header as HeaderT}; use runtime_primitives::generic::BlockId; -use substrate_primitives::{NativeOrEncoded, ExecutionContext}; +use substrate_primitives::{NativeOrEncoded, ExecutionContext, ed25519::Public as AuthorityId}; use authorities::AuthoritySet; use communication::GRANDPA_ENGINE_ID; @@ -290,7 +290,7 @@ impl Core for RuntimeApi { _: ExecutionContext, _: Option<()>, _: Vec, - ) -> Result>> { + ) -> Result>> { unimplemented!("Not required for testing!") } } @@ -323,7 +323,7 @@ impl GrandpaApi for RuntimeApi { _: ExecutionContext, _: Option<()>, _: Vec, - ) -> Result>> { + ) -> Result>> { if at == &BlockId::Number(0) { Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native) } else { @@ -370,7 +370,7 @@ impl GrandpaApi for RuntimeApi { const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); -fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(AuthorityId, u64)> { +fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(substrate_primitives::ed25519::Public, u64)> { keys.iter() .map(|key| AuthorityId(key.to_raw_public())) .map(|id| (id, 1)) @@ -734,7 +734,7 @@ fn justification_is_emitted_when_consensus_data_changes() { let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3); // import block#1 WITH consensus data change - let new_authorities = vec![AuthorityId::from_raw([42; 32])]; + let new_authorities = vec![substrate_primitives::sr25519::Public::from_raw([42; 32])]; net.peer(0).push_authorities_change_block(new_authorities); net.sync(); let net = Arc::new(Mutex::new(net)); diff --git a/substrate/core/inherents/src/lib.rs b/substrate/core/inherents/src/lib.rs index 7d2324bc93..87fa39fe06 100644 --- a/substrate/core/inherents/src/lib.rs +++ b/substrate/core/inherents/src/lib.rs @@ -114,7 +114,9 @@ impl InherentData { match self.data.get(identifier) { Some(inherent) => I::decode(&mut &inherent[..]) - .ok_or_else(|| "Could not decode requested inherent type!".into()) + .ok_or_else(|| { + "Could not decode requested inherent type!".into() + }) .map(Some), None => Ok(None) } diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml index 6899243eea..ca2e7621f6 100644 --- a/substrate/core/network/Cargo.toml +++ b/substrate/core/network/Cargo.toml @@ -40,4 +40,4 @@ consensus = { package = "substrate-consensus-common", path = "../../core/consens [features] default = [] -test-helpers = ["keyring", "test_client"] +test-helpers = ["keyring", "test_client", "consensus/test-helpers"] diff --git a/substrate/core/network/src/protocol.rs b/substrate/core/network/src/protocol.rs index 8f5c258474..de10d3d935 100644 --- a/substrate/core/network/src/protocol.rs +++ b/substrate/core/network/src/protocol.rs @@ -435,7 +435,10 @@ impl, H: ExHashT> Protocol { return false; }, #[cfg(any(test, feature = "test-helpers"))] - ProtocolMsg::Synchronize => self.network_chan.send(NetworkMsg::Synchronized), + ProtocolMsg::Synchronize => { + trace!(target: "sync", "handle_client_msg: received Synchronize msg"); + self.network_chan.send(NetworkMsg::Synchronized) + } } true } @@ -755,6 +758,11 @@ impl, H: ExHashT> Protocol { "Peer is on different chain (our genesis: {} theirs: {})", self.genesis_hash, status.genesis_hash ); + trace!( + target: "protocol", + "Peer is on different chain (our genesis: {} theirs: {})", + self.genesis_hash, status.genesis_hash + ); self.network_chan.send(NetworkMsg::ReportPeer( who, Severity::Bad(reason), @@ -763,6 +771,7 @@ impl, H: ExHashT> Protocol { } if status.version < MIN_VERSION && CURRENT_VERSION < status.min_supported_version { let reason = format!("Peer using unsupported protocol version {}", status.version); + trace!(target: "protocol", "Peer {:?} using unsupported protocol version {}", who, status.version); self.network_chan.send(NetworkMsg::ReportPeer( who, Severity::Bad(reason), diff --git a/substrate/core/network/src/service.rs b/substrate/core/network/src/service.rs index 28416fe7f4..4f4d3d838a 100644 --- a/substrate/core/network/src/service.rs +++ b/substrate/core/network/src/service.rs @@ -589,7 +589,7 @@ fn run_thread( } } } - + Ok(Async::NotReady) }) } diff --git a/substrate/core/network/src/sync.rs b/substrate/core/network/src/sync.rs index 491c5a3558..59b2aabeed 100644 --- a/substrate/core/network/src/sync.rs +++ b/substrate/core/network/src/sync.rs @@ -848,8 +848,9 @@ impl ChainSync { /// Handle new block announcement. pub(crate) fn on_block_announce(&mut self, protocol: &mut Context, who: PeerId, hash: B::Hash, header: &B::Header) { let number = *header.number(); + debug!(target: "sync", "Received block announcement with number {:?}", number); if number <= As::sa(0) { - trace!(target: "sync", "Ignored invalid block announcement from {}: {}", who, hash); + warn!(target: "sync", "Ignored invalid block announcement from {}: {}", who, hash); return; } let parent_status = block_status(&*protocol.client(), &self.queue_blocks, header.parent_hash().clone()).ok() diff --git a/substrate/core/network/src/test/mod.rs b/substrate/core/network/src/test/mod.rs index 5b375984b4..0954fbaed0 100644 --- a/substrate/core/network/src/test/mod.rs +++ b/substrate/core/network/src/test/mod.rs @@ -40,7 +40,7 @@ use futures::sync::{mpsc, oneshot}; use crate::message::Message; use network_libp2p::PeerId; use parking_lot::{Mutex, RwLock}; -use primitives::{H256, ed25519::Public as AuthorityId}; +use primitives::{H256, sr25519::Public as AuthorityId}; use crate::protocol::{ConnectedPeer, Context, FromNetworkMsg, Protocol, ProtocolMsg}; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{AuthorityIdFor, Block as BlockT, Digest, DigestItem, Header, NumberFor}; @@ -48,6 +48,7 @@ use runtime_primitives::{Justification, ConsensusEngineId}; use crate::service::{network_channel, NetworkChan, NetworkLink, NetworkMsg, NetworkPort, TransactionPool}; use crate::specialization::NetworkSpecialization; use test_client::{self, AccountKeyring}; +use log::debug; pub use test_client::runtime::{Block, Extrinsic, Hash, Transfer}; pub use test_client::TestClient; @@ -117,17 +118,20 @@ pub type PeersClient = client::Client> { link: NetworkLink, + + #[cfg(any(test, feature = "test-helpers"))] network_to_protocol_sender: Sender>, } impl> TestLink { fn new( protocol_sender: Sender>, - network_to_protocol_sender: Sender>, + _network_to_protocol_sender: Sender>, network_sender: NetworkChan ) -> TestLink { TestLink { - network_to_protocol_sender, + #[cfg(any(test, feature = "test-helpers"))] + network_to_protocol_sender: _network_to_protocol_sender, link: NetworkLink { protocol_sender, network_sender, @@ -165,8 +169,14 @@ impl> Link for TestLink { self.link.restart(); } + /// Send synchronization request to the block import channel. + /// + /// The caller should wait for the `Link::synchronized` call to ensure that it has synchronized + /// with `ImportQueue`. + #[cfg(any(test, feature = "test-helpers"))] fn synchronized(&self) { - let _ = self.network_to_protocol_sender.send(FromNetworkMsg::Synchronize); + trace!(target: "test_network", "Synchronizing"); + drop(self.network_to_protocol_sender.send(FromNetworkMsg::Synchronize)) } } @@ -225,12 +235,14 @@ impl> ProtocolChannel { /// Wait until synchronization response is generated by the protocol. pub fn wait_sync(&self) -> Result<(), RecvError> { + trace!(target: "test_network", "Waiting for sync"); loop { match self.protocol_to_network_receiver.receiver().recv() { Ok(NetworkMsg::Synchronized) => return Ok(()), Err(error) => return Err(error), Ok(msg) => self.buffered_messages.lock().push_back(msg), } + trace!(target: "test_network", "Retrying sync"); } } @@ -370,8 +382,11 @@ impl> Peer { } /// Synchronize with import queue. + #[cfg(any(test, feature = "test-helpers"))] fn import_queue_sync(&self) { + trace!(target: "test_network", "syncing this queue"); self.import_queue.synchronize(); + trace!(target: "test_network", "wating for sync to finish"); let _ = self.net_proto_channel.wait_sync(); } @@ -491,6 +506,7 @@ impl> Peer { let block = edit_block(builder); let hash = block.header.hash(); trace!( + target: "test_network", "Generating {}, (#{}, parent={})", hash, block.header.number, @@ -610,10 +626,12 @@ pub trait TestNetFactory: Sized { /// Create new test network with this many peers. fn new(n: usize) -> Self { + trace!(target: "test_network", "Creating test network"); let config = Self::default_config(); let mut net = Self::from_config(&config); - for _ in 0..n { + for i in 0..n { + trace!(target: "test_network", "Adding peer {}", i); net.add_peer(&config); } net @@ -681,6 +699,7 @@ pub trait TestNetFactory: Sized { } loop { + debug!(target: "test_network", "loop iteration"); // we only deliver Status messages during start let need_continue = self.route_single(true, None, &|msg| match *msg { NetworkMsg::Outgoing(_, crate::message::generic::Message::Status(_)) => true, @@ -706,6 +725,7 @@ pub trait TestNetFactory: Sized { let mut to_disconnect = HashSet::new(); let peers = self.peers(); for peer in peers { + debug!(target: "test_network", "checking peer"); if let Some(message) = peer.pending_message(message_filter) { match message { NetworkMsg::Outgoing(recipient_id, packet) => { @@ -744,10 +764,13 @@ pub trait TestNetFactory: Sized { } } } + debug!(target: "test_network", "syncing queues"); // make sure that the protocol(s) has processed all messages that have been queued self.peers().iter().for_each(|peer| peer.import_queue_sync()); + debug!(target: "test_network", "queues synced"); + had_messages } diff --git a/substrate/core/primitives/src/crypto.rs b/substrate/core/primitives/src/crypto.rs index dce30fad71..9cd71bb9b2 100644 --- a/substrate/core/primitives/src/crypto.rs +++ b/substrate/core/primitives/src/crypto.rs @@ -288,7 +288,7 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { /// /// For now it just specifies how to create a key from a phrase and derivation path. #[cfg(feature = "std")] -pub trait Pair: Sized { +pub trait Pair: Sized + 'static { /// TThe type which is used to encode a public key. type Public; diff --git a/substrate/core/rpc/src/chain/error.rs b/substrate/core/rpc/src/chain/error.rs index 81fe01a19d..723a21d773 100644 --- a/substrate/core/rpc/src/chain/error.rs +++ b/substrate/core/rpc/src/chain/error.rs @@ -18,16 +18,21 @@ use error_chain::*; use client; use crate::rpc; use crate::errors; +pub use internal_errors::*; -error_chain! { - foreign_links { - Client(client::error::Error) #[doc = "Client error"]; - } - errors { - /// Not implemented yet - Unimplemented { - description("not yet implemented"), - display("Method Not Implemented"), +#[allow(deprecated)] +mod internal_errors { + use super::*; + error_chain! { + foreign_links { + Client(client::error::Error) #[doc = "Client error"]; + } + errors { + /// Not implemented yet + Unimplemented { + description("not yet implemented"), + display("Method Not Implemented"), + } } } } diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index 5cf83921ad..5321116c95 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -221,7 +221,7 @@ fn should_return_runtime_version() { assert_eq!( ::serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(), - r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",2],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",3],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1],["0xf78b278be53f454c",1],["0x7801759919ee83e5",1]]}"# + r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",2],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",3],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1],["0xcbca25e39f142387",1],["0xf78b278be53f454c",1],["0x7801759919ee83e5",1]]}"# ); } @@ -246,3 +246,4 @@ fn should_notify_on_runtime_version_initially() { // no more notifications on this channel assert_eq!(core.block_on(next.into_future()).unwrap().0, None); } + diff --git a/substrate/core/sr-io/Cargo.toml b/substrate/core/sr-io/Cargo.toml index afbce1c81f..404bf80dea 100644 --- a/substrate/core/sr-io/Cargo.toml +++ b/substrate/core/sr-io/Cargo.toml @@ -11,7 +11,7 @@ rustc_version = "0.2" [dependencies] rstd = { package = "sr-std", path = "../sr-std", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } -codec = { package="parity-codec", version = "3.3", default-features = false } +codec = { package = "parity-codec", version = "3.5.1", default-features = false } hash-db = { version = "0.12", default-features = false } libsecp256k1 = { version = "0.2.1", optional = true } tiny-keccak = { version = "1.4.2", optional = true } diff --git a/substrate/core/sr-primitives/src/testing.rs b/substrate/core/sr-primitives/src/testing.rs index 763b68b180..6e650eca20 100644 --- a/substrate/core/sr-primitives/src/testing.rs +++ b/substrate/core/sr-primitives/src/testing.rs @@ -23,7 +23,7 @@ use crate::traits::{self, Checkable, Applyable, BlakeTwo256, Convert}; use crate::generic::DigestItem as GenDigestItem; pub use substrate_primitives::H256; use substrate_primitives::U256; -use substrate_primitives::ed25519::{Public as AuthorityId, Signature as AuthoritySignature}; +use substrate_primitives::sr25519::{Public as AuthorityId, Signature as AuthoritySignature}; /// Authority Id #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug)] diff --git a/substrate/core/sr-std/Cargo.toml b/substrate/core/sr-std/Cargo.toml index b941fea1d1..f973329077 100644 --- a/substrate/core/sr-std/Cargo.toml +++ b/substrate/core/sr-std/Cargo.toml @@ -6,7 +6,7 @@ build = "build.rs" edition = "2018" [build-dependencies] -rustc_version = "0.2" +rustc_version = "0.2.3" [features] default = ["std"] diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index 6b68f0ecde..fc1ccd140f 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -29,7 +29,7 @@ pub use client::{ExecutionStrategies, blockchain, backend, self}; pub use executor::{NativeExecutor, self}; pub use runtime; pub use consensus; -pub use keyring::{AuthorityKeyring, AccountKeyring}; +pub use keyring::{sr25519::Keyring as AuthorityKeyring, AccountKeyring}; use std::{sync::Arc, collections::HashMap}; use futures::future::FutureResult; diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 49767c30b9..610173406e 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -13,6 +13,7 @@ substrate-client = { path = "../client", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } inherents = { package = "substrate-inherents", path = "../inherents", default-features = false } consensus_aura = { package = "substrate-consensus-aura-primitives", path = "../consensus/aura/primitives", default-features = false } +consensus_babe = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../sr-io", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } @@ -49,6 +50,7 @@ std = [ "runtime_primitives/std", "runtime_version/std", "consensus_aura/std", + "consensus_babe/std", "primitives/std", "substrate-trie/std", "trie-db/std", diff --git a/substrate/core/test-runtime/src/genesismap.rs b/substrate/core/test-runtime/src/genesismap.rs index 849b67b678..cf7682102f 100644 --- a/substrate/core/test-runtime/src/genesismap.rs +++ b/substrate/core/test-runtime/src/genesismap.rs @@ -22,7 +22,7 @@ use super::AccountId; use parity_codec::{Encode, KeyedVec, Joiner}; use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys}; use runtime_primitives::traits::Block; -use primitives::ed25519::Public as AuthorityId; +use primitives::sr25519::Public as AuthorityId; /// Configuration of a general Substrate test genesis block. pub struct GenesisConfig { diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index f3cc321003..e7c6348495 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -43,7 +43,7 @@ use runtime_primitives::{ }; use runtime_version::RuntimeVersion; pub use primitives::hash::H256; -use primitives::{ed25519, sr25519, OpaqueMetadata}; +use primitives::{sr25519, OpaqueMetadata}; #[cfg(any(feature = "std", test))] use runtime_version::NativeVersion; use inherents::{CheckInherentsResult, InherentData}; @@ -142,7 +142,7 @@ impl Extrinsic { } /// The signature type used by authorities. -pub type AuthoritySignature = ed25519::Signature; +pub type AuthoritySignature = sr25519::Signature; /// The identity type used by authorities. pub type AuthorityId = ::Signer; /// The signature type used by accounts/transactions. @@ -448,6 +448,16 @@ cfg_if! { fn slot_duration() -> u64 { 1 } } + impl consensus_babe::BabeApi for Runtime { + fn startup_data() -> consensus_babe::BabeConfiguration { + consensus_babe::BabeConfiguration { + slot_duration: 1, + expected_block_time: 1, + threshold: std::u64::MAX, + } + } + } + impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(block: u64) { let ex = Extrinsic::IncludeData(block.encode()); @@ -457,7 +467,7 @@ cfg_if! { impl consensus_authorities::AuthoritiesApi for Runtime { fn authorities() -> Vec> { - crate::system::authorities() + system::authorities() } } } @@ -580,6 +590,16 @@ cfg_if! { fn slot_duration() -> u64 { 1 } } + impl consensus_babe::BabeApi for Runtime { + fn startup_data() -> consensus_babe::BabeConfiguration { + consensus_babe::BabeConfiguration { + slot_duration: 1, + expected_block_time: 1, + threshold: core::u64::MAX, + } + } + } + impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(block: u64) { let ex = Extrinsic::IncludeData(block.encode()); @@ -589,7 +609,7 @@ cfg_if! { impl consensus_authorities::AuthoritiesApi for Runtime { fn authorities() -> Vec> { - crate::system::authorities() + system::authorities() } } } diff --git a/substrate/core/test-runtime/src/system.rs b/substrate/core/test-runtime/src/system.rs index 75cd1dddf6..5345dc0cdb 100644 --- a/substrate/core/test-runtime/src/system.rs +++ b/substrate/core/test-runtime/src/system.rs @@ -27,7 +27,7 @@ use runtime_primitives::{ApplyError, ApplyOutcome, ApplyResult, transaction_vali use parity_codec::{KeyedVec, Encode}; use super::{AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest}; use primitives::{Blake2Hasher, storage::well_known_keys}; -use primitives::ed25519::Public as AuthorityId; +use primitives::sr25519::Public as AuthorityId; const NONCE_OF: &[u8] = b"nonce:"; const BALANCE_OF: &[u8] = b"balance:"; diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index e22fc83e18..c6510ab383 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -2433,6 +2433,16 @@ dependencies = [ "substrate-primitives 1.0.0", ] +[[package]] +name = "substrate-consensus-babe-primitives" +version = "1.0.0" +dependencies = [ + "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 1.0.0", + "substrate-client 1.0.0", + "substrate-consensus-slots 1.1.0", +] + [[package]] name = "substrate-consensus-common" version = "1.0.0" @@ -2450,6 +2460,23 @@ dependencies = [ "tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-consensus-slots" +version = "1.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 1.0.0", + "substrate-client 1.0.0", + "substrate-consensus-common 1.0.0", + "substrate-inherents 1.0.0", + "substrate-primitives 1.0.0", + "tokio 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-executor" version = "1.0.0" @@ -2596,6 +2623,7 @@ dependencies = [ "substrate-client 1.0.0", "substrate-consensus-aura-primitives 1.0.0", "substrate-consensus-authorities 1.0.0", + "substrate-consensus-babe-primitives 1.0.0", "substrate-inherents 1.0.0", "substrate-keyring 1.0.0", "substrate-offchain-primitives 0.1.0", diff --git a/substrate/core/test-runtime/wasm/build.sh b/substrate/core/test-runtime/wasm/build.sh index abca9a60e9..635532babf 100755 --- a/substrate/core/test-runtime/wasm/build.sh +++ b/substrate/core/test-runtime/wasm/build.sh @@ -9,5 +9,5 @@ fi CARGO_INCREMENTAL=0 RUSTFLAGS="-C link-arg=--export-table" $CARGO_CMD build --target=wasm32-unknown-unknown --release for i in substrate_test_runtime do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm + wasm-gc "target/wasm32-unknown-unknown/release/$i.wasm" "target/wasm32-unknown-unknown/release/$i.compact.wasm" done diff --git a/substrate/core/transaction-pool/graph/Cargo.toml b/substrate/core/transaction-pool/graph/Cargo.toml index 9090ef18b9..13078d7dd8 100644 --- a/substrate/core/transaction-pool/graph/Cargo.toml +++ b/substrate/core/transaction-pool/graph/Cargo.toml @@ -14,7 +14,7 @@ substrate-primitives = { path = "../../primitives" } sr-primitives = { path = "../../sr-primitives" } [dev-dependencies] -assert_matches = "1.1" -env_logger = "0.6" -parity-codec = "3.3" +assert_matches = "1.3.0" +env_logger = "0.6.1" +parity-codec = "3.5.1" test_runtime = { package = "substrate-test-runtime", path = "../../test-runtime" } diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 3ec297b17b..65eb3b8f80 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 67, - impl_version: 68, + spec_version: 69, + impl_version: 70, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/scripts/build.sh b/substrate/scripts/build.sh index 9cacf74dbb..46bc74b7a9 100755 --- a/substrate/scripts/build.sh +++ b/substrate/scripts/build.sh @@ -5,14 +5,14 @@ set -e PROJECT_ROOT=`git rev-parse --show-toplevel` -source `dirname "$0"`/common.sh +source "`dirname \"$0\"`/common.sh" export CARGO_INCREMENTAL=0 # Save current directory. pushd . -cd $ROOT +cd -- "$ROOT" for SRC in "${SRCS[@]}" do diff --git a/substrate/srml/babe/Cargo.toml b/substrate/srml/babe/Cargo.toml new file mode 100644 index 0000000000..e6d8be3ca9 --- /dev/null +++ b/substrate/srml/babe/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "srml-babe" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +hex-literal = "0.1.4" +parity-codec = { version = "3.5.1", default-features = false, features = ["derive"] } +serde = { version = "1.0.90", 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 } +srml-support = { path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } +timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false } +staking = { package = "srml-staking", path = "../staking", 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 } + +[dev-dependencies] +lazy_static = "1.3.0" +parking_lot = "0.7.1" +substrate-primitives = { path = "../../core/primitives" } +runtime_io = { package = "sr-io", path = "../../core/sr-io" } +consensus = { package = "srml-consensus", path = "../consensus" } + +[features] +default = ["std"] +std = [ + "serde", + "parity-codec/std", + "rstd/std", + "srml-support/std", + "primitives/std", + "system/std", + "timestamp/std", + "staking/std", + "inherents/std", + "babe-primitives/std", +] diff --git a/substrate/srml/babe/src/lib.rs b/substrate/srml/babe/src/lib.rs new file mode 100644 index 0000000000..e9b5426221 --- /dev/null +++ b/substrate/srml/babe/src/lib.rs @@ -0,0 +1,155 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Consensus extension module for BABE consensus. + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code, warnings)] +pub use timestamp; + +use rstd::{result, prelude::*}; +use srml_support::{decl_storage, decl_module}; +use primitives::traits::As; +use timestamp::{OnTimestampSet, Trait}; +#[cfg(feature = "std")] +use timestamp::TimestampInherentData; +use parity_codec::Decode; +use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError}; +#[cfg(feature = "std")] +use inherents::{InherentDataProviders, ProvideInherentData}; + +/// The BABE inherent identifier. +pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot"; + +/// The type of the BABE inherent. +pub type InherentType = u64; + +/// Auxiliary trait to extract BABE inherent data. +pub trait BabeInherentData { + /// Get BABE inherent data. + fn babe_inherent_data(&self) -> result::Result; + /// Replace BABE inherent data. + fn babe_replace_inherent_data(&mut self, new: InherentType); +} + +impl BabeInherentData for InherentData { + fn babe_inherent_data(&self) -> result::Result { + self.get_data(&INHERENT_IDENTIFIER) + .and_then(|r| r.ok_or_else(|| "BABE inherent data not found".into())) + } + + fn babe_replace_inherent_data(&mut self, new: InherentType) { + self.replace_data(INHERENT_IDENTIFIER, &new); + } +} + +/// Provides the slot duration inherent data for BABE. +#[cfg(feature = "std")] +pub struct InherentDataProvider { + slot_duration: u64, +} + +#[cfg(feature = "std")] +impl InherentDataProvider { + pub fn new(slot_duration: u64) -> Self { + Self { + slot_duration + } + } +} + +#[cfg(feature = "std")] +impl ProvideInherentData for InherentDataProvider { + fn on_register( + &self, + providers: &InherentDataProviders, + ) -> result::Result<(), RuntimeString> { + if !providers.has_provider(×tamp::INHERENT_IDENTIFIER) { + // Add the timestamp inherent data provider, as we require it. + providers.register_provider(timestamp::InherentDataProvider) + } else { + Ok(()) + } + } + + fn inherent_identifier(&self) -> &'static inherents::InherentIdentifier { + &INHERENT_IDENTIFIER + } + + fn provide_inherent_data( + &self, + inherent_data: &mut InherentData, + ) -> result::Result<(), RuntimeString> { + let timestamp = inherent_data.timestamp_inherent_data()?; + let slot_num = timestamp / self.slot_duration; + inherent_data.put_data(INHERENT_IDENTIFIER, &slot_num) + } + + fn error_to_string(&self, error: &[u8]) -> Option { + RuntimeString::decode(&mut &error[..]).map(Into::into) + } +} + +decl_storage! { + trait Store for Module as Babe { + // The last timestamp. + LastTimestamp get(last): T::Moment; + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { } +} + +impl Module { + /// Determine the BABE slot duration based on the Timestamp module configuration. + pub fn slot_duration() -> u64 { + // we double the minimum block-period so each author can always propose within + // the majority of their slot. + >::minimum_period().as_().saturating_mul(2) + } +} + +impl OnTimestampSet for Module { + fn on_timestamp_set(_moment: T::Moment) { } +} + +impl ProvideInherent for Module { + type Call = timestamp::Call; + type Error = MakeFatalError; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(_: &InherentData) -> Option { + None + } + + fn check_inherent(call: &Self::Call, data: &InherentData) -> result::Result<(), Self::Error> { + let timestamp = match call { + timestamp::Call::set(ref timestamp) => timestamp.clone(), + _ => return Ok(()), + }; + + let timestamp_based_slot = timestamp.as_() / Self::slot_duration(); + + let seal_slot = data.babe_inherent_data()?; + + if timestamp_based_slot == seal_slot { + Ok(()) + } else { + Err(RuntimeString::from("timestamp set in block doesn’t match slot in seal").into()) + } + } +} diff --git a/substrate/srml/consensus/src/lib.rs b/substrate/srml/consensus/src/lib.rs index ba56e6e4e3..031c489b8f 100644 --- a/substrate/srml/consensus/src/lib.rs +++ b/substrate/srml/consensus/src/lib.rs @@ -142,7 +142,7 @@ use inherents::{ }; #[cfg(any(feature = "std", test))] -use substrate_primitives::ed25519::Public as AuthorityId; +use substrate_primitives::sr25519::Public as AuthorityId; mod mock; mod tests;