diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs index caf9a89072..79d77612c7 100644 --- a/polkadot/api/src/full.rs +++ b/polkadot/api/src/full.rs @@ -25,8 +25,11 @@ use state_machine; use runtime::Address; use runtime_primitives::traits::AuxLookup; -use primitives::{AccountId, Block, Header, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; -use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; +use primitives::{ + AccountId, Block, Header, BlockId, Hash, Index, InherentData, + SessionKey, Timestamp, UncheckedExtrinsic, +}; +use primitives::parachain::{DutyRoster, Id as ParaId}; use {BlockBuilder, PolkadotApi, LocalPolkadotApi, ErrorKind, Error, Result}; @@ -132,20 +135,20 @@ impl> PolkadotApi for Client) -> Result { + fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result { let mut block_builder = self.new_block_at(at)?; - for inherent in self.inherent_extrinsics(at, timestamp, new_heads)? { + for inherent in self.inherent_extrinsics(at, inherent_data)? { block_builder.push(inherent)?; } Ok(block_builder) } - fn inherent_extrinsics(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec) -> Result> { + fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result> { use codec::{Encode, Decode}; with_runtime!(self, at, || { - let extrinsics = ::runtime::inherent_extrinsics(timestamp, new_heads); + let extrinsics = ::runtime::inherent_extrinsics(inherent_data); extrinsics.into_iter() .map(|x| x.encode()) // get encoded representation .map(|x| Decode::decode(&mut &x[..])) // get byte-vec equivalent to extrinsic @@ -216,7 +219,11 @@ mod tests { let client = client(); let id = BlockId::number(0); - let block_builder = client.build_block(&id, 1_000_000, Vec::new()).unwrap(); + let block_builder = client.build_block(&id, InherentData { + timestamp: 1_000_000, + parachain_heads: Vec::new(), + offline_indices: Vec::new(), + }).unwrap(); let block = block_builder.bake().unwrap(); assert_eq!(block.header.number, 1); @@ -228,7 +235,11 @@ mod tests { let client = client(); let id = BlockId::number(0); - let inherent = client.inherent_extrinsics(&id, 1_000_000, Vec::new()).unwrap(); + let inherent = client.inherent_extrinsics(&id, InherentData { + timestamp: 1_000_000, + parachain_heads: Vec::new(), + offline_indices: Vec::new(), + }).unwrap(); let mut block_builder = client.new_block_at(&id).unwrap(); for extrinsic in inherent { diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index 27bea18a0d..8c1e4b1de8 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -38,10 +38,12 @@ extern crate substrate_keyring as keyring; pub mod full; pub mod light; -use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, - UncheckedExtrinsic}; +use primitives::{ + AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, + UncheckedExtrinsic, InherentData, +}; use runtime::Address; -use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; +use primitives::parachain::{DutyRoster, Id as ParaId}; error_chain! { errors { @@ -128,11 +130,11 @@ pub trait PolkadotApi { fn evaluate_block(&self, at: &BlockId, block: Block) -> Result; /// Build a block on top of the given, with inherent extrinsics pre-pushed. - fn build_block(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec) -> Result; + fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result; /// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given. /// This may vary by runtime and will fail if a runtime doesn't follow the same API. - fn inherent_extrinsics(&self, at: &BlockId, timestamp: Timestamp, new_heads: Vec) -> Result>; + fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result>; } /// Mark for all Polkadot API implementations, that are making use of state data, stored locally. diff --git a/polkadot/api/src/light.rs b/polkadot/api/src/light.rs index 7e2015c0b7..a281cde6e3 100644 --- a/polkadot/api/src/light.rs +++ b/polkadot/api/src/light.rs @@ -20,9 +20,12 @@ use std::sync::Arc; use client::backend::{Backend, RemoteBackend}; use client::{Client, CallExecutor}; use codec::Decode; -use primitives::{AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp, UncheckedExtrinsic}; +use primitives::{ + AccountId, Block, BlockId, Hash, Index, InherentData, + SessionKey, Timestamp, UncheckedExtrinsic, +}; use runtime::Address; -use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; +use primitives::parachain::{DutyRoster, Id as ParaId}; use {PolkadotApi, BlockBuilder, RemotePolkadotApi, Result, ErrorKind}; /// Light block builder. TODO: make this work (efficiently) @@ -92,11 +95,11 @@ impl, E: CallExecutor> PolkadotApi for RemotePolkadotAp Err(ErrorKind::UnknownRuntime.into()) } - fn build_block(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result { + fn build_block(&self, _at: &BlockId, _inherent: InherentData) -> Result { Err(ErrorKind::UnknownRuntime.into()) } - fn inherent_extrinsics(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result>> { + fn inherent_extrinsics(&self, _at: &BlockId, _inherent: InherentData) -> Result>> { Err(ErrorKind::UnknownRuntime.into()) } } diff --git a/polkadot/consensus/Cargo.toml b/polkadot/consensus/Cargo.toml index 68ad826551..e50551c518 100644 --- a/polkadot/consensus/Cargo.toml +++ b/polkadot/consensus/Cargo.toml @@ -11,7 +11,7 @@ ed25519 = { path = "../../substrate/ed25519" } error-chain = "0.12" log = "0.3" exit-future = "0.1" -rhododendron = "0.2" +rhododendron = "0.3" polkadot-api = { path = "../api" } polkadot-parachain = { path = "../parachain" } polkadot-primitives = { path = "../primitives" } diff --git a/polkadot/consensus/src/dynamic_inclusion.rs b/polkadot/consensus/src/dynamic_inclusion.rs index bec2bd0fa8..232acea238 100644 --- a/polkadot/consensus/src/dynamic_inclusion.rs +++ b/polkadot/consensus/src/dynamic_inclusion.rs @@ -74,6 +74,9 @@ impl DynamicInclusion { Some(now + until) } } + + /// Get the start instant. + pub fn started_at(&self) -> Instant { self.start } } #[cfg(test)] diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 4bef0fceaf..2d9639a8cd 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -67,7 +67,7 @@ use std::time::{Duration, Instant}; use codec::{Decode, Encode}; use polkadot_api::PolkadotApi; -use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey}; +use polkadot_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey}; use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt, CandidateSignature}; use primitives::AuthorityId; use transaction_pool::TransactionPool; @@ -78,20 +78,26 @@ use futures::prelude::*; use futures::future; use collation::CollationFetch; use dynamic_inclusion::DynamicInclusion; +use parking_lot::RwLock; pub use self::collation::{validate_collation, Collators}; pub use self::error::{ErrorKind, Error}; +pub use self::offline_tracker::OfflineTracker; pub use self::shared_table::{SharedTable, StatementProducer, ProducedStatements, Statement, SignedStatement, GenericStatement}; pub use service::Service; mod dynamic_inclusion; mod evaluation; mod error; +mod offline_tracker; mod service; mod shared_table; pub mod collation; +/// Shared offline validator tracker. +pub type SharedOfflineTracker = Arc>; + // block size limit. const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; @@ -236,6 +242,8 @@ pub struct ProposerFactory { pub handle: TaskExecutor, /// The duration after which parachain-empty blocks will be allowed. pub parachain_empty_duration: Duration, + /// Offline-tracker. + pub offline: SharedOfflineTracker, } impl bft::Environment for ProposerFactory @@ -251,10 +259,11 @@ impl bft::Environment for ProposerFactory type Output = N::Output; type Error = Error; - fn init(&self, + fn init( + &self, parent_header: &Header, authorities: &[AuthorityId], - sign_with: Arc + sign_with: Arc, ) -> Result<(Self::Proposer, Self::Input, Self::Output), Error> { use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; @@ -265,6 +274,9 @@ impl bft::Environment for ProposerFactory let random_seed = self.client.random_seed(&id)?; let random_seed = BlakeTwo256::hash(&*random_seed); + let validators = self.client.validators(&id)?; + self.offline.write().note_new_block(&validators[..]); + let (group_info, local_duty) = make_group_info( duty_roster, authorities, @@ -316,6 +328,8 @@ impl bft::Environment for ProposerFactory random_seed, table, transaction_pool: self.transaction_pool.clone(), + offline: self.offline.clone(), + validators, _drop_signal: drop_signal, }; @@ -370,9 +384,22 @@ pub struct Proposer { random_seed: Hash, table: Arc, transaction_pool: Arc>, + offline: SharedOfflineTracker, + validators: Vec, _drop_signal: exit_future::Signal, } +impl Proposer { + fn primary_index(&self, round_number: usize, len: usize) -> usize { + use primitives::uint::U256; + + let big_len = U256::from(len); + let offset = U256::from_big_endian(&self.random_seed.0) % big_len; + let offset = offset.low_u64() as usize + round_number; + offset % len + } +} + impl bft::Proposer for Proposer where C: PolkadotApi + Send + Sync, @@ -408,6 +435,8 @@ impl bft::Proposer for Proposer client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), table: self.table.clone(), + offline: self.offline.clone(), + validators: self.validators.clone(), timing, }) } @@ -482,6 +511,13 @@ impl bft::Proposer for Proposer includability_tracker.join(temporary_delay) }; + // refuse to vote if this block says a validator is offline that we + // think isn't. + let offline = proposal.noted_offline(); + if !self.offline.read().check_consistency(&self.validators[..], offline) { + return Box::new(futures::empty()); + } + // evaluate whether the block is actually valid. // TODO: is it better to delay this until the delays are finished? let evaluated = self.client @@ -503,13 +539,8 @@ impl bft::Proposer for Proposer } fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { - use primitives::uint::U256; - - let len: U256 = authorities.len().into(); - let offset = U256::from_big_endian(&self.random_seed.0) % len; - let offset = offset.low_u64() as usize + round_number; - - let proposer = authorities[offset % authorities.len()].clone(); + let offset = self.primary_index(round_number, authorities.len()); + let proposer = authorities[offset].clone(); trace!(target: "bft", "proposer for round {} is {}", round_number, proposer); proposer @@ -578,6 +609,34 @@ impl bft::Proposer for Proposer .expect("locally signed extrinsic is valid; qed"); } } + + fn on_round_end(&self, round_number: usize, was_proposed: bool) { + let primary_validator = self.validators[ + self.primary_index(round_number, self.validators.len()) + ]; + + // alter the message based on whether we think the empty proposer was forced to skip the round. + // this is determined by checking if our local validator would have been forced to skip the round. + let consider_online = was_proposed || { + let forced_delay = self.dynamic_inclusion.acceptable_in(Instant::now(), self.table.includable_count()); + match forced_delay { + None => info!( + "Potential Offline Validator: {:?} failed to propose during assigned slot: {}", + primary_validator, + round_number, + ), + Some(_) => info!( + "Potential Offline Validator {:?} potentially forced to skip assigned slot: {}", + primary_validator, + round_number, + ), + } + + forced_delay.is_some() + }; + + self.offline.write().note_round_end(primary_validator, consider_online); + } } fn current_timestamp() -> Timestamp { @@ -634,16 +693,42 @@ pub struct CreateProposal { transaction_pool: Arc>, table: Arc, timing: ProposalTiming, + validators: Vec, + offline: SharedOfflineTracker, } impl CreateProposal where C: PolkadotApi { fn propose_with(&self, candidates: Vec) -> Result { use polkadot_api::BlockBuilder; use runtime_primitives::traits::{Hash as HashT, BlakeTwo256}; + use polkadot_primitives::InherentData; + + const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); // TODO: handle case when current timestamp behind that in state. let timestamp = current_timestamp(); - let mut block_builder = self.client.build_block(&self.parent_id, timestamp, candidates)?; + + let elapsed_since_start = self.timing.dynamic_inclusion.started_at().elapsed(); + let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { + Vec::new() + } else { + self.offline.read().reports(&self.validators[..]) + }; + + if !offline_indices.is_empty() { + info!( + "Submitting offline validators {:?} for slash-vote", + offline_indices.iter().map(|&i| self.validators[i as usize]).collect::>(), + ) + } + + let inherent_data = InherentData { + timestamp, + parachain_heads: candidates, + offline_indices, + }; + + let mut block_builder = self.client.build_block(&self.parent_id, inherent_data)?; { let mut unqueue_invalid = Vec::new(); diff --git a/polkadot/consensus/src/offline_tracker.rs b/polkadot/consensus/src/offline_tracker.rs new file mode 100644 index 0000000000..efb317ea5c --- /dev/null +++ b/polkadot/consensus/src/offline_tracker.rs @@ -0,0 +1,137 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Tracks offline validators. + +use polkadot_primitives::AccountId; + +use std::collections::HashMap; +use std::time::{Instant, Duration}; + +// time before we report a validator. +const REPORT_TIME: Duration = Duration::from_secs(60 * 5); + +struct Observed { + last_round_end: Instant, + offline_since: Instant, +} + +impl Observed { + fn new() -> Observed { + let now = Instant::now(); + Observed { + last_round_end: now, + offline_since: now, + } + } + + fn note_round_end(&mut self, was_online: bool) { + let now = Instant::now(); + + self.last_round_end = now; + if was_online { + self.offline_since = now; + } + } + + fn is_active(&self) -> bool { + // can happen if clocks are not monotonic + if self.offline_since > self.last_round_end { return true } + self.last_round_end.duration_since(self.offline_since) < REPORT_TIME + } +} + +/// Tracks offline validators and can issue a report for those offline. +pub struct OfflineTracker { + observed: HashMap, +} + +impl OfflineTracker { + /// Create a new tracker. + pub fn new() -> Self { + OfflineTracker { observed: HashMap::new() } + } + + /// Note new consensus is starting with the given set of validators. + pub fn note_new_block(&mut self, validators: &[AccountId]) { + use std::collections::HashSet; + + let set: HashSet<_> = validators.iter().cloned().collect(); + self.observed.retain(|k, _| set.contains(k)); + } + + /// Note that a round has ended. + pub fn note_round_end(&mut self, validator: AccountId, was_online: bool) { + self.observed.entry(validator) + .or_insert_with(Observed::new) + .note_round_end(was_online); + } + + /// Generate a vector of indices for offline account IDs. + pub fn reports(&self, validators: &[AccountId]) -> Vec { + validators.iter() + .enumerate() + .filter_map(|(i, v)| if self.is_online(v) { + None + } else { + Some(i as u32) + }) + .collect() + } + + /// Whether reports on a validator set are consistent with our view of things. + pub fn check_consistency(&self, validators: &[AccountId], reports: &[u32]) -> bool { + reports.iter().cloned().all(|r| { + let v = match validators.get(r as usize) { + Some(v) => v, + None => return false, + }; + + // we must think all validators reported externally are offline. + let thinks_online = self.is_online(v); + !thinks_online + }) + } + + fn is_online(&self, v: &AccountId) -> bool { + self.observed.get(v).map(Observed::is_active).unwrap_or(true) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validator_offline() { + let mut tracker = OfflineTracker::new(); + let v = [0; 32].into(); + let v2 = [1; 32].into(); + let v3 = [2; 32].into(); + tracker.note_round_end(v, true); + tracker.note_round_end(v2, true); + tracker.note_round_end(v3, true); + + let slash_time = REPORT_TIME + Duration::from_secs(5); + tracker.observed.get_mut(&v).unwrap().offline_since -= slash_time; + tracker.observed.get_mut(&v2).unwrap().offline_since -= slash_time; + + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 1]); + + tracker.note_new_block(&[v, v3]); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); + } +} diff --git a/polkadot/consensus/src/service.rs b/polkadot/consensus/src/service.rs index ca7561bd4d..d0d89786c3 100644 --- a/polkadot/consensus/src/service.rs +++ b/polkadot/consensus/src/service.rs @@ -113,6 +113,9 @@ impl Service { N::TableRouter: Send + 'static, ::Future: Send + 'static, { + use parking_lot::RwLock; + use super::OfflineTracker; + let (signal, exit) = ::exit_future::signal(); let thread = thread::spawn(move || { let mut runtime = LocalRuntime::new().expect("Could not create local runtime"); @@ -125,6 +128,7 @@ impl Service { network, parachain_empty_duration, handle: thread_pool, + offline: Arc::new(RwLock::new(OfflineTracker::new())), }; let bft_service = Arc::new(BftService::new(client.clone(), key, factory)); diff --git a/polkadot/network/Cargo.toml b/polkadot/network/Cargo.toml index 37d36ea205..e674fad158 100644 --- a/polkadot/network/Cargo.toml +++ b/polkadot/network/Cargo.toml @@ -17,4 +17,4 @@ ed25519 = { path = "../../substrate/ed25519" } futures = "0.1" tokio = "0.1.7" log = "0.4" -rhododendron = "0.2" +rhododendron = "0.3" diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 83118799bb..5185227720 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -120,3 +120,32 @@ impl Encode for Log { self.0.encode_to(dest) } } + +/// Inherent data to include in a block. +pub struct InherentData { + /// Current timestamp. + pub timestamp: Timestamp, + /// Parachain heads update. + pub parachain_heads: Vec<::parachain::CandidateReceipt>, + /// Indices of offline validators. + pub offline_indices: Vec, +} + +impl Decode for InherentData { + fn decode(input: &mut I) -> Option { + let (timestamp, parachain_heads, offline_indices) = Decode::decode(input)?; + Some(InherentData { + timestamp, + parachain_heads, + offline_indices, + }) + } +} + +impl Encode for InherentData { + fn encode_to(&self, dest: &mut T) { + self.timestamp.encode_to(dest); + self.parachain_heads.encode_to(dest); + self.offline_indices.encode_to(dest); + } +} diff --git a/polkadot/runtime/src/checked_block.rs b/polkadot/runtime/src/checked_block.rs index 4ea7bd0e88..d193d26963 100644 --- a/polkadot/runtime/src/checked_block.rs +++ b/polkadot/runtime/src/checked_block.rs @@ -16,9 +16,10 @@ //! Typesafe block interaction. -use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION}; +use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION, NOTE_OFFLINE_POSITION}; use timestamp::Call as TimestampCall; use parachains::Call as ParachainsCall; +use session::Call as SessionCall; use primitives::parachain::CandidateReceipt; /// Provides a type-safe wrapper around a structurally valid block. @@ -47,6 +48,7 @@ impl CheckedBlock { }); if !has_heads { return Err(block) } + Ok(CheckedBlock { inner: block, file_line: None, @@ -88,6 +90,14 @@ impl CheckedBlock { } } + /// Extract the noted offline validator indices (if any) from the block. + pub fn noted_offline(&self) -> &[u32] { + self.inner.extrinsics.get(NOTE_OFFLINE_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Session(SessionCall::note_offline(ref x)) => Some(&x[..]), + _ => None, + }).unwrap_or(&[]) + } + /// Convert into inner block. pub fn into_inner(self) -> Block { self.inner } } diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 7c366e30e7..dfe8e61048 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -85,6 +85,8 @@ pub use primitives::Header; pub const TIMESTAMP_SET_POSITION: u32 = 0; /// The position of the parachains set extrinsic. pub const PARACHAINS_SET_POSITION: u32 = 1; +/// The position of the offline nodes noting extrinsic. +pub const NOTE_OFFLINE_POSITION: u32 = 2; /// The address format for describing accounts. pub type Address = staking::Address; @@ -110,7 +112,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: ver_str!("polkadot"), impl_name: ver_str!("parity-polkadot"), authoring_version: 1, - spec_version: 2, + spec_version: 3, impl_version: 0, }; @@ -160,6 +162,7 @@ impl Convert for SessionKeyConversion { } impl session::Trait for Concrete { + const NOTE_OFFLINE_POSITION: u32 = NOTE_OFFLINE_POSITION; type ConvertAccountIdToSessionKey = SessionKeyConversion; type OnSessionChange = Staking; } @@ -247,7 +250,7 @@ pub mod api { apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), execute_block => |block| super::Executive::execute_block(block), finalise_block => |()| super::Executive::finalise_block(), - inherent_extrinsics => |(timestamp, heads)| super::inherent_extrinsics(timestamp, heads), + inherent_extrinsics => |inherent| super::inherent_extrinsics(inherent), validator_count => |()| super::Session::validator_count(), validators => |()| super::Session::validators() ); diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs index 4ac5f3f2a1..16cdcdca33 100644 --- a/polkadot/runtime/src/parachains.rs +++ b/polkadot/runtime/src/parachains.rs @@ -265,6 +265,7 @@ mod tests { type Header = Header; } impl session::Trait for Test { + const NOTE_OFFLINE_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = (); } diff --git a/polkadot/runtime/src/utils.rs b/polkadot/runtime/src/utils.rs index 1531590ff5..acef060925 100644 --- a/polkadot/runtime/src/utils.rs +++ b/polkadot/runtime/src/utils.rs @@ -19,30 +19,33 @@ use rstd::prelude::*; use super::{Call, UncheckedExtrinsic, Extrinsic, Staking}; use runtime_primitives::traits::{Checkable, AuxLookup}; -use primitives::parachain::CandidateReceipt; use timestamp::Call as TimestampCall; use parachains::Call as ParachainsCall; +use session::Call as SessionCall; /// Produces the list of inherent extrinsics. -pub fn inherent_extrinsics(timestamp: ::primitives::Timestamp, parachain_heads: Vec) -> Vec { - vec![ - UncheckedExtrinsic::new( - Extrinsic { - signed: Default::default(), - function: Call::Timestamp(TimestampCall::set(timestamp)), - index: 0, - }, - Default::default() - ), - UncheckedExtrinsic::new( - Extrinsic { - signed: Default::default(), - function: Call::Parachains(ParachainsCall::set_heads(parachain_heads)), - index: 0, - }, - Default::default() - ) - ] +pub fn inherent_extrinsics(data: ::primitives::InherentData) -> Vec { + let make_inherent = |function| UncheckedExtrinsic::new( + Extrinsic { + signed: Default::default(), + function, + index: 0, + }, + Default::default(), + ); + + let mut inherent = vec![ + make_inherent(Call::Timestamp(TimestampCall::set(data.timestamp))), + make_inherent(Call::Parachains(ParachainsCall::set_heads(data.parachain_heads))), + ]; + + if !data.offline_indices.is_empty() { + inherent.push(make_inherent( + Call::Session(SessionCall::note_offline(data.offline_indices)) + )); + } + + inherent } /// Checks an unchecked extrinsic for validity. diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index b90d8e14e8..f69011af31 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -416,7 +416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -691,8 +691,11 @@ dependencies = [ [[package]] name = "smallvec" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "stable_deref_trait" @@ -1213,7 +1216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "fba5be06346c5200249c8c8ca4ccba4a09e8747c71c16e420bd359a0db4d8f91" "checksum serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)" = "79e4620ba6fbe051fc7506fab6f84205823564d55da18d55b695160fb3479cd8" -"checksum smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03dab98ab5ded3a8b43b2c80751194608d0b2aa0f1d46cf95d1c35e192844aa7" +"checksum smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "211a489e65e94b103926d2054ae515a1cdb5d515ea0ef414fee23b7e043ce748" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum syn 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfd71b2be5a58ee30a6f8ea355ba8290d397131c00dfa55c3d34e6e13db5101" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 7e6dbc7c1b..dba5dc7d2d 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index 4029d032ca..717beffa27 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/polkadot/transaction-pool/src/lib.rs b/polkadot/transaction-pool/src/lib.rs index 265f72ba99..447d97d004 100644 --- a/polkadot/transaction-pool/src/lib.rs +++ b/polkadot/transaction-pool/src/lib.rs @@ -286,7 +286,7 @@ impl<'a, A> txpool::Verifier for Verifier<'a, A> where let encoded = uxt.encode(); let (encoded_size, hash) = (encoded.len(), BlakeTwo256::hash(&encoded)); - + debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded)); let inner = match uxt.clone().check_with(|a| self.lookup(a)) { @@ -446,10 +446,10 @@ mod tests { use substrate_keyring::Keyring::{self, *}; use codec::{Decode, Encode}; use polkadot_api::{PolkadotApi, BlockBuilder, Result}; - use primitives::{AccountId, AccountIndex, Block, BlockId, Hash, Index, SessionKey, Timestamp, + use primitives::{AccountId, AccountIndex, Block, BlockId, Hash, Index, SessionKey, UncheckedExtrinsic as FutureProofUncheckedExtrinsic}; use runtime::{RawAddress, Call, TimestampCall, BareExtrinsic, Extrinsic, UncheckedExtrinsic}; - use primitives::parachain::{CandidateReceipt, DutyRoster, Id as ParaId}; + use primitives::parachain::{DutyRoster, Id as ParaId}; use substrate_runtime_primitives::{MaybeUnsigned, generic}; struct TestBlockBuilder; @@ -494,8 +494,8 @@ mod tests { fn active_parachains(&self, _at: &BlockId) -> Result> { unimplemented!() } fn parachain_code(&self, _at: &BlockId, _parachain: ParaId) -> Result>> { unimplemented!() } fn parachain_head(&self, _at: &BlockId, _parachain: ParaId) -> Result>> { unimplemented!() } - fn build_block(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result { unimplemented!() } - fn inherent_extrinsics(&self, _at: &BlockId, _timestamp: Timestamp, _new_heads: Vec) -> Result>> { unimplemented!() } + fn build_block(&self, _at: &BlockId, _inherent: ::primitives::InherentData) -> Result { unimplemented!() } + fn inherent_extrinsics(&self, _at: &BlockId, _inherent: ::primitives::InherentData) -> Result>> { unimplemented!() } fn index(&self, _at: &BlockId, _account: AccountId) -> Result { Ok((_account[0] as u32) + number_of(_at))