From b50596428eefac2afe6c08809d5a33632c88522c Mon Sep 17 00:00:00 2001
From: Robert Habermeier <rphmeier@gmail.com>
Date: Sat, 24 Aug 2019 17:17:01 +0200
Subject: [PATCH] GRANDPA links set IDs to sessions. (#3472)

* introduce some type aliases for round and set-id

* overhaul session "changed" flag and document better

* do_initialize in BABE when getting new session

* grandpa module tracks set IDs

* update runtime versions

* doc comment -> comment

* Include docs fixes from Gav

Co-Authored-By: Gavin Wood <gavin@parity.io>

* some more review changes

* fix srml-grandpa compilation
---
 .../finality-grandpa/primitives/src/lib.rs    |   6 +
 .../core/finality-grandpa/src/aux_schema.rs   |  20 ++--
 .../finality-grandpa/src/communication/mod.rs |  36 +++---
 .../core/finality-grandpa/src/environment.rs  |  50 +++++----
 substrate/core/finality-grandpa/src/lib.rs    |   8 +-
 .../core/finality-grandpa/src/light_import.rs |   4 +-
 substrate/node/runtime/src/lib.rs             |   2 +-
 substrate/srml/babe/src/lib.rs                |   2 +
 substrate/srml/grandpa/src/lib.rs             |  64 +++++++----
 substrate/srml/session/src/lib.rs             | 103 +++++++++++++-----
 substrate/srml/session/src/mock.rs            |  29 +++--
 substrate/srml/staking/src/lib.rs             |   4 +
 12 files changed, 214 insertions(+), 114 deletions(-)

diff --git a/substrate/core/finality-grandpa/primitives/src/lib.rs b/substrate/core/finality-grandpa/primitives/src/lib.rs
index b92444e2629..1f103a548d1 100644
--- a/substrate/core/finality-grandpa/primitives/src/lib.rs
+++ b/substrate/core/finality-grandpa/primitives/src/lib.rs
@@ -52,6 +52,12 @@ pub type AuthorityWeight = u64;
 /// The index of an authority.
 pub type AuthorityIndex = u64;
 
+/// The identifier of a GRANDPA set.
+pub type SetId = u64;
+
+/// The round indicator.
+pub type RoundNumber = u64;
+
 /// A scheduled change of authority set.
 #[cfg_attr(feature = "std", derive(Debug, Serialize))]
 #[derive(Clone, Eq, PartialEq, Encode, Decode)]
diff --git a/substrate/core/finality-grandpa/src/aux_schema.rs b/substrate/core/finality-grandpa/src/aux_schema.rs
index 78c1741d519..599604c1d32 100644
--- a/substrate/core/finality-grandpa/src/aux_schema.rs
+++ b/substrate/core/finality-grandpa/src/aux_schema.rs
@@ -26,7 +26,7 @@ use grandpa::round::State as RoundState;
 use sr_primitives::traits::{Block as BlockT, NumberFor};
 use log::{info, warn};
 use substrate_telemetry::{telemetry, CONSENSUS_INFO};
-use fg_primitives::AuthorityId;
+use fg_primitives::{AuthorityId, AuthorityWeight, SetId, RoundNumber};
 
 use crate::authorities::{AuthoritySet, SharedAuthoritySet, PendingChange, DelayKind};
 use crate::consensus_changes::{SharedConsensusChanges, ConsensusChanges};
@@ -47,16 +47,16 @@ const CURRENT_VERSION: u32 = 2;
 #[cfg_attr(test, derive(PartialEq))]
 pub enum V1VoterSetState<H, N> {
 	/// The voter set state, currently paused.
-	Paused(u64, RoundState<H, N>),
+	Paused(RoundNumber, RoundState<H, N>),
 	/// The voter set state, currently live.
-	Live(u64, RoundState<H, N>),
+	Live(RoundNumber, RoundState<H, N>),
 }
 
-type V0VoterSetState<H, N> = (u64, RoundState<H, N>);
+type V0VoterSetState<H, N> = (RoundNumber, RoundState<H, N>);
 
 #[derive(Debug, Clone, Encode, Decode, PartialEq)]
 struct V0PendingChange<H, N> {
-	next_authorities: Vec<(AuthorityId, u64)>,
+	next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
 	delay: N,
 	canon_height: N,
 	canon_hash: H,
@@ -64,8 +64,8 @@ struct V0PendingChange<H, N> {
 
 #[derive(Debug, Clone, Encode, Decode, PartialEq)]
 struct V0AuthoritySet<H, N> {
-	current_authorities: Vec<(AuthorityId, u64)>,
-	set_id: u64,
+	current_authorities: Vec<(AuthorityId, AuthorityWeight)>,
+	set_id: SetId,
 	pending_changes: Vec<V0PendingChange<H, N>>,
 }
 
@@ -267,7 +267,7 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
 	-> ClientResult<PersistentData<Block>>
 	where
 		B: AuxStore,
-		G: FnOnce() -> ClientResult<Vec<(AuthorityId, u64)>>,
+		G: FnOnce() -> ClientResult<Vec<(AuthorityId, AuthorityWeight)>>,
 {
 	let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
 	let consensus_changes = load_decode(backend, CONSENSUS_CHANGES_KEY)?
@@ -448,7 +448,7 @@ mod test {
 
 		let authorities = vec![(AuthorityId::default(), 100)];
 		let set_id = 3;
-		let round_number: u64 = 42;
+		let round_number: RoundNumber = 42;
 		let round_state = RoundState::<H256, u64> {
 			prevote_ghost: Some((H256::random(), 32)),
 			finalized: None,
@@ -536,7 +536,7 @@ mod test {
 
 		let authorities = vec![(AuthorityId::default(), 100)];
 		let set_id = 3;
-		let round_number: u64 = 42;
+		let round_number: RoundNumber = 42;
 		let round_state = RoundState::<H256, u64> {
 			prevote_ghost: Some((H256::random(), 32)),
 			finalized: None,
diff --git a/substrate/core/finality-grandpa/src/communication/mod.rs b/substrate/core/finality-grandpa/src/communication/mod.rs
index a4b149fac46..732c14c1a9c 100644
--- a/substrate/core/finality-grandpa/src/communication/mod.rs
+++ b/substrate/core/finality-grandpa/src/communication/mod.rs
@@ -50,7 +50,9 @@ use crate::environment::HasVoted;
 use gossip::{
 	GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator
 };
-use fg_primitives::{AuthorityPair, AuthorityId, AuthoritySignature};
+use fg_primitives::{
+	AuthorityPair, AuthorityId, AuthoritySignature, SetId as SetIdNumber, RoundNumber,
+};
 
 pub mod gossip;
 mod periodic;
@@ -129,12 +131,12 @@ pub trait Network<Block: BlockT>: Clone + Send + 'static {
 }
 
 /// Create a unique topic for a round and set-id combo.
-pub(crate) fn round_topic<B: BlockT>(round: u64, set_id: u64) -> B::Hash {
+pub(crate) fn round_topic<B: BlockT>(round: RoundNumber, set_id: SetIdNumber) -> B::Hash {
 	<<B::Header as HeaderT>::Hashing as HashT>::hash(format!("{}-{}", set_id, round).as_bytes())
 }
 
 /// Create a unique topic for global messages on a set ID.
-pub(crate) fn global_topic<B: BlockT>(set_id: u64) -> B::Hash {
+pub(crate) fn global_topic<B: BlockT>(set_id: SetIdNumber) -> B::Hash {
 	<<B::Header as HeaderT>::Hashing as HashT>::hash(format!("{}-GLOBAL", set_id).as_bytes())
 }
 
@@ -618,25 +620,25 @@ impl<B: BlockT, N: Network<B>> Clone for NetworkBridge<B, N> {
 	}
 }
 
-fn localized_payload<E: Encode>(round: u64, set_id: u64, message: &E) -> Vec<u8> {
+fn localized_payload<E: Encode>(round: RoundNumber, set_id: SetIdNumber, message: &E) -> Vec<u8> {
 	(message, round, set_id).encode()
 }
 
-/// Type-safe wrapper around u64 when indicating that it's a round number.
+/// Type-safe wrapper around a round number.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Encode, Decode)]
-pub struct Round(pub u64);
+pub struct Round(pub RoundNumber);
 
-/// Type-safe wrapper around u64 when indicating that it's a set ID.
+/// Type-safe wrapper around a set ID.
 #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Encode, Decode)]
-pub struct SetId(pub u64);
+pub struct SetId(pub SetIdNumber);
 
 // check a message.
 pub(crate) fn check_message_sig<Block: BlockT>(
 	message: &Message<Block>,
 	id: &AuthorityId,
 	signature: &AuthoritySignature,
-	round: u64,
-	set_id: u64,
+	round: RoundNumber,
+	set_id: SetIdNumber,
 ) -> Result<(), ()> {
 	let as_public = id.clone();
 	let encoded_raw = localized_payload(round, set_id, message);
@@ -656,8 +658,8 @@ pub(crate) fn check_message_sig<Block: BlockT>(
 /// `ed25519` and `BLS` signatures (which we might use in the future), care must
 /// be taken when switching to different key types.
 struct OutgoingMessages<Block: BlockT, N: Network<Block>> {
-	round: u64,
-	set_id: u64,
+	round: RoundNumber,
+	set_id: SetIdNumber,
 	locals: Option<(AuthorityPair, AuthorityId)>,
 	sender: mpsc::UnboundedSender<SignedMessage<Block>>,
 	network: N,
@@ -851,8 +853,8 @@ fn check_catch_up<Block: BlockT>(
 
 	fn check_signatures<'a, B, I>(
 		messages: I,
-		round: u64,
-		set_id: u64,
+		round: RoundNumber,
+		set_id: SetIdNumber,
 		mut signatures_checked: usize,
 	) -> Result<usize, i32> where
 		B: BlockT,
@@ -919,7 +921,7 @@ impl<Block: BlockT, N: Network<Block>> CommitsOut<Block, N> {
 	/// Create a new commit output stream.
 	pub(crate) fn new(
 		network: N,
-		set_id: u64,
+		set_id: SetIdNumber,
 		is_voter: bool,
 		gossip_validator: Arc<GossipValidator<Block>>,
 	) -> Self {
@@ -933,10 +935,10 @@ impl<Block: BlockT, N: Network<Block>> CommitsOut<Block, N> {
 }
 
 impl<Block: BlockT, N: Network<Block>> Sink for CommitsOut<Block, N> {
-	type SinkItem = (u64, Commit<Block>);
+	type SinkItem = (RoundNumber, Commit<Block>);
 	type SinkError = Error;
 
-	fn start_send(&mut self, input: (u64, Commit<Block>)) -> StartSend<Self::SinkItem, Error> {
+	fn start_send(&mut self, input: (RoundNumber, Commit<Block>)) -> StartSend<Self::SinkItem, Error> {
 		if !self.is_voter {
 			return Ok(AsyncSink::Ready);
 		}
diff --git a/substrate/core/finality-grandpa/src/environment.rs b/substrate/core/finality-grandpa/src/environment.rs
index b20e8ab5dfc..c0474cb0368 100644
--- a/substrate/core/finality-grandpa/src/environment.rs
+++ b/substrate/core/finality-grandpa/src/environment.rs
@@ -51,7 +51,7 @@ use crate::authorities::{AuthoritySet, SharedAuthoritySet};
 use crate::consensus_changes::SharedConsensusChanges;
 use crate::justification::GrandpaJustification;
 use crate::until_imported::UntilVoteTargetImported;
-use fg_primitives::{AuthorityId, AuthoritySignature};
+use fg_primitives::{AuthorityId, AuthoritySignature, SetId, RoundNumber};
 
 type HistoricalVotes<Block> = grandpa::HistoricalVotes<
 	<Block as BlockT>::Hash,
@@ -65,7 +65,7 @@ type HistoricalVotes<Block> = grandpa::HistoricalVotes<
 #[derive(Debug, Clone, Decode, Encode, PartialEq)]
 pub struct CompletedRound<Block: BlockT> {
 	/// The round number.
-	pub number: u64,
+	pub number: RoundNumber,
 	/// The round state (prevote ghost, estimate, finalized, etc.)
 	pub state: RoundState<Block::Hash, NumberFor<Block>>,
 	/// The target block base used for voting in the round.
@@ -80,7 +80,7 @@ pub struct CompletedRound<Block: BlockT> {
 #[derive(Debug, Clone, PartialEq)]
 pub struct CompletedRounds<Block: BlockT> {
 	rounds: Vec<CompletedRound<Block>>,
-	set_id: u64,
+	set_id: SetId,
 	voters: Vec<AuthorityId>,
 }
 
@@ -100,7 +100,7 @@ impl<Block: BlockT> codec::EncodeLike for CompletedRounds<Block> {}
 
 impl<Block: BlockT> Decode for CompletedRounds<Block> {
 	fn decode<I: codec::Input>(value: &mut I) -> Result<Self, codec::Error> {
-		<(Vec<CompletedRound<Block>>, u64, Vec<AuthorityId>)>::decode(value)
+		<(Vec<CompletedRound<Block>>, SetId, Vec<AuthorityId>)>::decode(value)
 			.map(|(rounds, set_id, voters)| CompletedRounds {
 				rounds: rounds.into(),
 				set_id,
@@ -113,7 +113,7 @@ impl<Block: BlockT> CompletedRounds<Block> {
 	/// Create a new completed rounds tracker with NUM_LAST_COMPLETED_ROUNDS capacity.
 	pub(crate) fn new(
 		genesis: CompletedRound<Block>,
-		set_id: u64,
+		set_id: SetId,
 		voters: &AuthoritySet<Block::Hash, NumberFor<Block>>,
 	)
 		-> CompletedRounds<Block>
@@ -126,7 +126,7 @@ impl<Block: BlockT> CompletedRounds<Block> {
 	}
 
 	/// Get the set-id and voter set of the completed rounds.
-	pub fn set_info(&self) -> (u64, &[AuthorityId]) {
+	pub fn set_info(&self) -> (SetId, &[AuthorityId]) {
 		(self.set_id, &self.voters[..])
 	}
 
@@ -162,7 +162,7 @@ impl<Block: BlockT> CompletedRounds<Block> {
 
 /// A map with voter status information for currently live rounds,
 /// which votes have we cast and what are they.
-pub type CurrentRounds<Block> = BTreeMap<u64, HasVoted<Block>>;
+pub type CurrentRounds<Block> = BTreeMap<RoundNumber, HasVoted<Block>>;
 
 /// The state of the current voter set, whether it is currently active or not
 /// and information related to the previously completed rounds. Current round
@@ -190,7 +190,7 @@ impl<Block: BlockT> VoterSetState<Block> {
 	/// the given genesis state and the given authorities. Round 1 is added as a
 	/// current round (with state `HasVoted::No`).
 	pub(crate) fn live(
-		set_id: u64,
+		set_id: SetId,
 		authority_set: &AuthoritySet<Block::Hash, NumberFor<Block>>,
 		genesis_state: (Block::Hash, NumberFor<Block>),
 	) -> VoterSetState<Block> {
@@ -237,7 +237,7 @@ impl<Block: BlockT> VoterSetState<Block> {
 
 	/// Returns the voter set state validating that it includes the given round
 	/// in current rounds and that the voter isn't paused.
-	pub fn with_current_round(&self, round: u64)
+	pub fn with_current_round(&self, round: RoundNumber)
 		-> Result<(&CompletedRounds<Block>, &CurrentRounds<Block>), Error>
 	{
 		if let VoterSetState::Live { completed_rounds, current_rounds } = self {
@@ -344,7 +344,7 @@ impl<Block: BlockT> SharedVoterSetState<Block> {
 	}
 
 	/// Return vote status information for the current round.
-	pub(crate) fn has_voted(&self, round: u64) -> HasVoted<Block> {
+	pub(crate) fn has_voted(&self, round: RoundNumber) -> HasVoted<Block> {
 		match &*self.inner.read() {
 			VoterSetState::Live { current_rounds, .. } => {
 				current_rounds.get(&round).and_then(|has_voted| match has_voted {
@@ -375,7 +375,7 @@ pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
 	pub(crate) authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
 	pub(crate) consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
 	pub(crate) network: crate::communication::NetworkBridge<Block, N>,
-	pub(crate) set_id: u64,
+	pub(crate) set_id: SetId,
 	pub(crate) voter_set_state: SharedVoterSetState<Block>,
 }
 
@@ -554,7 +554,7 @@ where
 
 	fn round_data(
 		&self,
-		round: u64
+		round: RoundNumber,
 	) -> voter::RoundData<Self::Id, Self::Timer, Self::In, Self::Out> {
 		let now = Instant::now();
 		let prevote_timer = Delay::new(now + self.config.gossip_duration * 2);
@@ -601,7 +601,7 @@ where
 		}
 	}
 
-	fn proposed(&self, round: u64, propose: PrimaryPropose<Block>) -> Result<(), Self::Error> {
+	fn proposed(&self, round: RoundNumber, propose: PrimaryPropose<Block>) -> Result<(), Self::Error> {
 		let local_id = crate::is_voter(&self.voters, &self.config.keystore);
 
 		let local_id = match local_id {
@@ -641,7 +641,7 @@ where
 		Ok(())
 	}
 
-	fn prevoted(&self, round: u64, prevote: Prevote<Block>) -> Result<(), Self::Error> {
+	fn prevoted(&self, round: RoundNumber, prevote: Prevote<Block>) -> Result<(), Self::Error> {
 		let local_id = crate::is_voter(&self.voters, &self.config.keystore);
 
 		let local_id = match local_id {
@@ -683,7 +683,7 @@ where
 		Ok(())
 	}
 
-	fn precommitted(&self, round: u64, precommit: Precommit<Block>) -> Result<(), Self::Error> {
+	fn precommitted(&self, round: RoundNumber, precommit: Precommit<Block>) -> Result<(), Self::Error> {
 		let local_id = crate::is_voter(&self.voters, &self.config.keystore);
 
 		let local_id = match local_id {
@@ -737,7 +737,7 @@ where
 
 	fn completed(
 		&self,
-		round: u64,
+		round: RoundNumber,
 		state: RoundState<Block::Hash, NumberFor<Block>>,
 		base: (Block::Hash, NumberFor<Block>),
 		historical_votes: &HistoricalVotes<Block>,
@@ -794,7 +794,13 @@ where
 		Ok(())
 	}
 
-	fn finalize_block(&self, hash: Block::Hash, number: NumberFor<Block>, round: u64, commit: Commit<Block>) -> Result<(), Self::Error> {
+	fn finalize_block(
+		&self,
+		hash: Block::Hash,
+		number: NumberFor<Block>,
+		round: RoundNumber,
+		commit: Commit<Block>,
+	) -> Result<(), Self::Error> {
 		finalize_block(
 			&*self.inner,
 			&self.authority_set,
@@ -818,7 +824,7 @@ where
 
 	fn prevote_equivocation(
 		&self,
-		_round: u64,
+		_round: RoundNumber,
 		equivocation: ::grandpa::Equivocation<Self::Id, Prevote<Block>, Self::Signature>
 	) {
 		warn!(target: "afg", "Detected prevote equivocation in the finality worker: {:?}", equivocation);
@@ -827,7 +833,7 @@ where
 
 	fn precommit_equivocation(
 		&self,
-		_round: u64,
+		_round: RoundNumber,
 		equivocation: Equivocation<Self::Id, Precommit<Block>, Self::Signature>
 	) {
 		warn!(target: "afg", "Detected precommit equivocation in the finality worker: {:?}", equivocation);
@@ -837,11 +843,11 @@ where
 
 pub(crate) enum JustificationOrCommit<Block: BlockT> {
 	Justification(GrandpaJustification<Block>),
-	Commit((u64, Commit<Block>)),
+	Commit((RoundNumber, Commit<Block>)),
 }
 
-impl<Block: BlockT> From<(u64, Commit<Block>)> for JustificationOrCommit<Block> {
-	fn from(commit: (u64, Commit<Block>)) -> JustificationOrCommit<Block> {
+impl<Block: BlockT> From<(RoundNumber, Commit<Block>)> for JustificationOrCommit<Block> {
+	fn from(commit: (RoundNumber, Commit<Block>)) -> JustificationOrCommit<Block> {
 		JustificationOrCommit::Commit(commit)
 	}
 }
diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs
index f6c11dd6348..b79b120e357 100644
--- a/substrate/core/finality-grandpa/src/lib.rs
+++ b/substrate/core/finality-grandpa/src/lib.rs
@@ -108,7 +108,7 @@ use import::GrandpaBlockImport;
 use until_imported::UntilGlobalMessageBlocksImported;
 use communication::NetworkBridge;
 use service::TelemetryOnConnect;
-use fg_primitives::AuthoritySignature;
+use fg_primitives::{AuthoritySignature, SetId, AuthorityWeight};
 
 // Re-export these two because it's just so damn convenient.
 pub use fg_primitives::{AuthorityId, ScheduledChange};
@@ -267,8 +267,8 @@ impl<B, E, Block: BlockT<Hash=H256>, RA> BlockStatus<Block> for Arc<Client<B, E,
 pub(crate) struct NewAuthoritySet<H, N> {
 	pub(crate) canon_number: N,
 	pub(crate) canon_hash: H,
-	pub(crate) set_id: u64,
-	pub(crate) authorities: Vec<(AuthorityId, u64)>,
+	pub(crate) set_id: SetId,
+	pub(crate) authorities: Vec<(AuthorityId, AuthorityWeight)>,
 }
 
 /// Commands issued to the voter.
@@ -399,7 +399,7 @@ where
 }
 
 fn global_communication<Block: BlockT<Hash=H256>, B, E, N, RA>(
-	set_id: u64,
+	set_id: SetId,
 	voters: &Arc<VoterSet<AuthorityId>>,
 	client: &Arc<Client<B, E, Block, RA>>,
 	network: &NetworkBridge<Block, N>,
diff --git a/substrate/core/finality-grandpa/src/light_import.rs b/substrate/core/finality-grandpa/src/light_import.rs
index dbdabe96294..4d5381f1cc4 100644
--- a/substrate/core/finality-grandpa/src/light_import.rs
+++ b/substrate/core/finality-grandpa/src/light_import.rs
@@ -36,7 +36,7 @@ use sr_primitives::Justification;
 use sr_primitives::traits::{
 	NumberFor, Block as BlockT, Header as HeaderT, ProvideRuntimeApi, DigestFor,
 };
-use fg_primitives::{GrandpaApi, AuthorityId};
+use fg_primitives::{self, GrandpaApi, AuthorityId};
 use sr_primitives::generic::BlockId;
 use primitives::{H256, Blake2Hasher};
 
@@ -192,7 +192,7 @@ impl LightAuthoritySet {
 	/// Get a genesis set with given authorities.
 	pub fn genesis(initial: Vec<(AuthorityId, u64)>) -> Self {
 		LightAuthoritySet {
-			set_id: 0,
+			set_id: fg_primitives::SetId::default(),
 			authorities: initial,
 		}
 	}
diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs
index 01dff2b7ae8..c8496fa3b15 100644
--- a/substrate/node/runtime/src/lib.rs
+++ b/substrate/node/runtime/src/lib.rs
@@ -80,7 +80,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	// and set impl_version to equal spec_version. If only runtime
 	// implementation changes and behavior does not, then leave spec_version as
 	// is and increment impl_version.
-	spec_version: 149,
+	spec_version: 150,
 	impl_version: 150,
 	apis: RUNTIME_API_VERSIONS,
 };
diff --git a/substrate/srml/babe/src/lib.rs b/substrate/srml/babe/src/lib.rs
index b58fb26b5a1..0f439d489e8 100644
--- a/substrate/srml/babe/src/lib.rs
+++ b/substrate/srml/babe/src/lib.rs
@@ -445,6 +445,8 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
 	fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I)
 		where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
 	{
+		Self::do_initialize();
+
 		// Update epoch index
 		let epoch_index = EpochIndex::get()
 			.checked_add(1)
diff --git a/substrate/srml/grandpa/src/lib.rs b/substrate/srml/grandpa/src/lib.rs
index d64939ae0a7..5fc354eab96 100644
--- a/substrate/srml/grandpa/src/lib.rs
+++ b/substrate/srml/grandpa/src/lib.rs
@@ -33,7 +33,8 @@ pub use substrate_finality_grandpa_primitives as fg_primitives;
 use rstd::prelude::*;
 use codec::{self as codec, Encode, Decode, Error};
 use srml_support::{
-	decl_event, decl_storage, decl_module, dispatch::Result, storage::StorageValue
+	decl_event, decl_storage, decl_module, dispatch::Result,
+	storage::StorageValue, storage::StorageMap,
 };
 use sr_primitives::{
 	generic::{DigestItem, OpaqueDigestItemId}, traits::Zero,
@@ -43,7 +44,7 @@ use sr_staking_primitives::{
 	SessionIndex,
 	offence::{Offence, Kind},
 };
-use fg_primitives::{ScheduledChange, ConsensusLog, GRANDPA_ENGINE_ID};
+use fg_primitives::{GRANDPA_ENGINE_ID, ScheduledChange, ConsensusLog, SetId, RoundNumber};
 pub use fg_primitives::{AuthorityId, AuthorityWeight};
 use system::{ensure_signed, DigestOf};
 
@@ -65,7 +66,7 @@ pub struct OldStoredPendingChange<N> {
 	/// The delay in blocks until it will be applied.
 	pub delay: N,
 	/// The next authority set.
-	pub next_authorities: Vec<(AuthorityId, u64)>,
+	pub next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
 }
 
 /// A stored pending change.
@@ -76,7 +77,7 @@ pub struct StoredPendingChange<N> {
 	/// The delay in blocks until it will be applied.
 	pub delay: N,
 	/// The next authority set.
-	pub next_authorities: Vec<(AuthorityId, u64)>,
+	pub next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
 	/// If defined it means the change was forced and the given block number
 	/// indicates the median last finalized block when the change was signaled.
 	pub forced: Option<N>,
@@ -127,7 +128,7 @@ pub enum StoredState<N> {
 decl_event!(
 	pub enum Event {
 		/// New authority set has been applied.
-		NewAuthorities(Vec<(AuthorityId, u64)>),
+		NewAuthorities(Vec<(AuthorityId, AuthorityWeight)>),
 		/// Current authority set has been paused.
 		Paused,
 		/// Current authority set has been resumed.
@@ -151,6 +152,13 @@ decl_storage! {
 
 		/// `true` if we are currently stalled.
 		Stalled get(stalled): Option<(T::BlockNumber, T::BlockNumber)>;
+
+		/// The number of changes (both in terms of keys and underlying economic responsibilities)
+		/// in the "set" of Grandpa validators from genesis.
+		CurrentSetId get(current_set_id) build(|_| fg_primitives::SetId::default()): SetId;
+
+		/// A mapping from grandpa set ID to the index of the *most recent* session for which its members were responsible.
+		SetIdSession get(session_for_set): map SetId => Option<SessionIndex>;
 	}
 	add_extra_genesis {
 		config(authorities): Vec<(AuthorityId, AuthorityWeight)>;
@@ -243,7 +251,7 @@ decl_module! {
 
 impl<T: Trait> Module<T> {
 	/// Get the current set of authorities, along with their respective weights.
-	pub fn grandpa_authorities() -> Vec<(AuthorityId, u64)> {
+	pub fn grandpa_authorities() -> Vec<(AuthorityId, AuthorityWeight)> {
 		Authorities::get()
 	}
 
@@ -292,7 +300,7 @@ impl<T: Trait> Module<T> {
 	/// No change should be signaled while any change is pending. Returns
 	/// an error if a change is already pending.
 	pub fn schedule_change(
-		next_authorities: Vec<(AuthorityId, u64)>,
+		next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
 		in_blocks: T::BlockNumber,
 		forced: Option<T::BlockNumber>,
 	) -> Result {
@@ -337,29 +345,34 @@ impl<T: Trait> Module<T> {
 }
 
 impl<T: Trait> Module<T> {
+	/// Attempt to extract a GRANDPA log from a generic digest.
 	pub fn grandpa_log(digest: &DigestOf<T>) -> Option<ConsensusLog<T::BlockNumber>> {
 		let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
 		digest.convert_first(|l| l.try_to::<ConsensusLog<T::BlockNumber>>(id))
 	}
 
+	/// Attempt to extract a pending set-change signal from a digest.
 	pub fn pending_change(digest: &DigestOf<T>)
 		-> Option<ScheduledChange<T::BlockNumber>>
 	{
 		Self::grandpa_log(digest).and_then(|signal| signal.try_into_change())
 	}
 
+	/// Attempt to extract a forced set-change signal from a digest.
 	pub fn forced_change(digest: &DigestOf<T>)
 		-> Option<(T::BlockNumber, ScheduledChange<T::BlockNumber>)>
 	{
 		Self::grandpa_log(digest).and_then(|signal| signal.try_into_forced_change())
 	}
 
+	/// Attempt to extract a pause signal from a digest.
 	pub fn pending_pause(digest: &DigestOf<T>)
 		-> Option<T::BlockNumber>
 	{
 		Self::grandpa_log(digest).and_then(|signal| signal.try_into_pause())
 	}
 
+	/// Attempt to extract a resume signal from a digest.
 	pub fn pending_resume(digest: &DigestOf<T>)
 		-> Option<T::BlockNumber>
 	{
@@ -367,7 +380,9 @@ impl<T: Trait> Module<T> {
 	}
 }
 
-impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
+impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T>
+	where T: session::Trait
+{
 	type Key = AuthorityId;
 
 	fn on_genesis_session<'a, I: 'a>(validators: I)
@@ -380,18 +395,27 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
 	fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
 		where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
 	{
-		// instant changes
-		if changed {
+		// Always issue a change if `session` says that the validators have changed.
+		// Even if their session keys are the same as before, the underyling economic
+		// identities have changed.
+		let current_set_id = if changed {
 			let next_authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
-			let last_authorities = <Module<T>>::grandpa_authorities();
-			if next_authorities != last_authorities {
-				if let Some((further_wait, median)) = <Stalled<T>>::take() {
-					let _ = Self::schedule_change(next_authorities, further_wait, Some(median));
-				} else {
-					let _ = Self::schedule_change(next_authorities, Zero::zero(), None);
-				}
+			if let Some((further_wait, median)) = <Stalled<T>>::take() {
+				let _ = Self::schedule_change(next_authorities, further_wait, Some(median));
+			} else {
+				let _ = Self::schedule_change(next_authorities, Zero::zero(), None);
 			}
-		}
+			CurrentSetId::mutate(|s| { *s += 1; *s })
+		} else {
+			// nothing's changed, neither economic conditions nor session keys. update the pointer
+			// of the current set.
+			Self::current_set_id()
+		};
+
+		// if we didn't issue a change, we update the mapping to note that the current
+		// set corresponds to the latest equivalent session (i.e. now).
+		let session_index = <session::Module<T>>::current_index();
+		SetIdSession::insert(current_set_id, &session_index);
 	}
 
 	fn on_disabled(i: usize) {
@@ -412,8 +436,8 @@ impl<T: Trait> finality_tracker::OnFinalizationStalled<T::BlockNumber> for Modul
 #[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)]
 struct GrandpaTimeSlot {
 	// The order of these matters for `derive(Ord)`.
-	set_id: u64,
-	round: u64,
+	set_id: SetId,
+	round: RoundNumber,
 }
 
 // TODO [slashing]: Integrate this.
diff --git a/substrate/srml/session/src/lib.rs b/substrate/srml/session/src/lib.rs
index f8e13d529b8..6a09591e014 100644
--- a/substrate/srml/session/src/lib.rs
+++ b/substrate/srml/session/src/lib.rs
@@ -170,6 +170,12 @@ impl<
 pub trait OnSessionEnding<ValidatorId> {
 	/// Handle the fact that the session is ending, and optionally provide the new validator set.
 	///
+	/// Even if the validator-set is the same as before, if any underlying economic
+	/// conditions have changed (i.e. stake-weights), the new validator set must be returned.
+	/// This is necessary for consensus engines making use of the session module to
+	/// issue a validator-set change so misbehavior can be provably associated with the new
+	/// economic conditions as opposed to the old.
+	///
 	/// `ending_index` is the index of the currently ending session.
 	/// The returned validator set, if any, will not be applied until `will_apply_at`.
 	/// `will_apply_at` is guaranteed to be at least `ending_index + 1`, since session indices don't
@@ -192,7 +198,11 @@ pub trait SessionHandler<ValidatorId> {
 	/// should provide the same validator set.
 	fn on_genesis_session<Ks: OpaqueKeys>(validators: &[(ValidatorId, Ks)]);
 
-	/// Session set has changed; act appropriately.
+	/// Session set has changed; act appropriately. Note that this can be called
+	/// before initialization of your module.
+	///
+	/// `changed` is true whenever any of the session keys or underlying economic
+	/// identities or weightings behind those keys has changed.
 	fn on_new_session<Ks: OpaqueKeys>(
 		changed: bool,
 		validators: &[(ValidatorId, Ks)],
@@ -217,11 +227,19 @@ pub trait OneSessionHandler<ValidatorId> {
 	fn on_genesis_session<'a, I: 'a>(validators: I)
 		where I: Iterator<Item=(&'a ValidatorId, Self::Key)>, ValidatorId: 'a;
 
-	/// Session set has changed; act appropriately.
+	/// Session set has changed; act appropriately. Note that this can be called
+	/// before initialization of your module.
+	///
+	/// `changed` is true when at least one of the session keys
+	/// or the underlying economic identities/distribution behind one the
+	/// session keys has changed, false otherwise.
+	///
+	/// The `validators` are the validators of the incoming session, and `queued_validators`
+	/// will follow.
 	fn on_new_session<'a, I: 'a>(
-		_changed: bool,
-		_validators: I,
-		_queued_validators: I
+		changed: bool,
+		validators: I,
+		queued_validators: I,
 	) where I: Iterator<Item=(&'a ValidatorId, Self::Key)>, ValidatorId: 'a;
 
 
@@ -341,10 +359,8 @@ decl_storage! {
 		/// Current index of the session.
 		CurrentIndex get(current_index): SessionIndex;
 
-		/// True if anything has changed in this session.
-		Changed: bool;
-
-		/// Queued keys changed.
+		/// True if the underlying economic identities or weighting behind the validators
+		/// has changed in the queued validator set.
 		QueuedChanged: bool;
 
 		/// The queued keys for the next session. When the next session begins, these keys
@@ -443,13 +459,10 @@ decl_module! {
 
 			Self::do_set_keys(&who, keys)?;
 
-			// Something changed.
-			Changed::put(true);
-
 			Ok(())
 		}
 
-		/// Called when a block is finalized. Will rotate session if it is the last
+		/// Called when a block is initialized. Will rotate session if it is the last
 		/// block of the current session.
 		fn on_initialize(n: T::BlockNumber) {
 			if T::ShouldEndSession::should_end_session(n) {
@@ -467,7 +480,6 @@ impl<T: Trait> Module<T> {
 		let session_index = CurrentIndex::get();
 
 		let changed = QueuedChanged::get();
-		let mut next_changed = Changed::take();
 
 		// Get queued session keys and validators.
 		let session_keys = <QueuedKeys<T>>::get();
@@ -479,12 +491,16 @@ impl<T: Trait> Module<T> {
 		let applied_at = session_index + 2;
 
 		// Get next validator set.
-		let maybe_validators = T::OnSessionEnding::on_session_ending(session_index, applied_at);
-		let next_validators = if let Some(validators) = maybe_validators {
-			next_changed = true;
-			validators
+		let maybe_next_validators = T::OnSessionEnding::on_session_ending(session_index, applied_at);
+		let (next_validators, next_identities_changed)
+			= if let Some(validators) = maybe_next_validators
+		{
+			// NOTE: as per the documentation on `OnSessionEnding`, we consider
+			// the validator set as having changed even if the validators are the
+			// same as before, as underlying economic conditions may have changed.
+			(validators, true)
 		} else {
-			<Validators<T>>::get()
+			(<Validators<T>>::get(), false)
 		};
 
 		// Increment session index.
@@ -492,9 +508,34 @@ impl<T: Trait> Module<T> {
 		CurrentIndex::put(session_index);
 
 		// Queue next session keys.
-		let queued_amalgamated = next_validators.into_iter()
-			.map(|a| { let k = Self::load_keys(&a).unwrap_or_default(); (a, k) })
-			.collect::<Vec<_>>();
+		let (queued_amalgamated, next_changed) = {
+			// until we are certain there has been a change, iterate the prior
+			// validators along with the current and check for changes
+			let mut changed = next_identities_changed;
+
+			let mut now_session_keys = session_keys.iter();
+			let mut check_next_changed = |keys: &T::Keys| {
+				if changed { return }
+				// since a new validator set always leads to `changed` starting
+				// as true, we can ensure that `now_session_keys` and `next_validators`
+				// have the same length. this function is called once per iteration.
+				if let Some(&(_, ref old_keys)) = now_session_keys.next() {
+					if old_keys != keys {
+						changed = true;
+						return
+					}
+				}
+			};
+			let queued_amalgamated = next_validators.into_iter()
+				.map(|a| {
+					let k = Self::load_keys(&a).unwrap_or_default();
+					check_next_changed(&k);
+					(a, k)
+				})
+				.collect::<Vec<_>>();
+
+			(queued_amalgamated, changed)
+		};
 
 		<QueuedKeys<T>>::put(queued_amalgamated.clone());
 		QueuedChanged::put(next_changed);
@@ -503,13 +544,16 @@ impl<T: Trait> Module<T> {
 		Self::deposit_event(Event::NewSession(session_index));
 
 		// Tell everyone about the new session keys.
-		T::SessionHandler::on_new_session::<T::Keys>(changed, &session_keys, &queued_amalgamated);
+		T::SessionHandler::on_new_session::<T::Keys>(
+			changed,
+			&session_keys,
+			&queued_amalgamated,
+		);
 	}
 
 	/// Disable the validator of index `i`.
 	pub fn disable_index(i: usize) {
 		T::SessionHandler::on_disabled(i);
-		Changed::put(true);
 	}
 
 	/// Disable the validator identified by `c`. (If using with the staking module, this would be
@@ -554,8 +598,6 @@ impl<T: Trait> Module<T> {
 				let key_data = old_keys.get_raw(id);
 				Self::clear_key_owner(id, key_data);
 			}
-
-			Changed::put(true);
 		}
 	}
 
@@ -668,8 +710,6 @@ mod tests {
 			Session::on_free_balance_zero(&1);
 			assert_eq!(Session::load_keys(&1), None);
 			assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), None);
-
-			assert!(Changed::get());
 		})
 	}
 
@@ -816,9 +856,16 @@ mod tests {
 			initialize_block(6);
 			assert!(!session_changed());
 
+			// changing the keys of a validator leads to change.
+			assert_ok!(Session::set_keys(Origin::signed(69), UintAuthorityId(69).into(), vec![]));
 			force_new_session();
 			initialize_block(7);
 			assert!(session_changed());
+
+			// while changing the keys of a non-validator does not.
+			force_new_session();
+			initialize_block(7);
+			assert!(!session_changed());
 		});
 	}
 
diff --git a/substrate/srml/session/src/mock.rs b/substrate/srml/session/src/mock.rs
index c5608e1a545..13fcb54754c 100644
--- a/substrate/srml/session/src/mock.rs
+++ b/substrate/srml/session/src/mock.rs
@@ -44,6 +44,7 @@ impl_outer_origin! {
 }
 
 thread_local! {
+	pub static VALIDATORS: RefCell<Vec<u64>> = RefCell::new(vec![1, 2, 3]);
 	pub static NEXT_VALIDATORS: RefCell<Vec<u64>> = RefCell::new(vec![1, 2, 3]);
 	pub static AUTHORITIES: RefCell<Vec<UintAuthorityId>> =
 		RefCell::new(vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
@@ -51,6 +52,7 @@ thread_local! {
 	pub static SESSION_LENGTH: RefCell<u64> = RefCell::new(2);
 	pub static SESSION_CHANGED: RefCell<bool> = RefCell::new(false);
 	pub static TEST_SESSION_CHANGED: RefCell<bool> = RefCell::new(false);
+	pub static DISABLED: RefCell<bool> = RefCell::new(false);
 }
 
 pub struct TestShouldEndSession;
@@ -76,14 +78,24 @@ impl SessionHandler<u64> for TestSessionHandler {
 				.collect()
 		);
 	}
-	fn on_disabled(_validator_index: usize) {}
+	fn on_disabled(_validator_index: usize) {
+		DISABLED.with(|l| *l.borrow_mut() = true)
+	}
 }
 
 pub struct TestOnSessionEnding;
 impl OnSessionEnding<u64> for TestOnSessionEnding {
 	fn on_session_ending(_: SessionIndex, _: SessionIndex) -> Option<Vec<u64>> {
 		if !TEST_SESSION_CHANGED.with(|l| *l.borrow()) {
-			Some(NEXT_VALIDATORS.with(|l| l.borrow().clone()))
+			VALIDATORS.with(|v| {
+				let mut v = v.borrow_mut();
+				*v = NEXT_VALIDATORS.with(|l| l.borrow().clone());
+				Some(v.clone())
+			})
+		} else if DISABLED.with(|l| std::mem::replace(&mut *l.borrow_mut(), false)) {
+			// If there was a disabled validator, underlying conditions have changed
+			// so we return `Some`.
+			Some(VALIDATORS.with(|v| v.borrow().clone()))
 		} else {
 			None
 		}
@@ -92,16 +104,13 @@ impl OnSessionEnding<u64> for TestOnSessionEnding {
 
 #[cfg(feature = "historical")]
 impl crate::historical::OnSessionEnding<u64, u64> for TestOnSessionEnding {
-	fn on_session_ending(_: SessionIndex, _: SessionIndex)
+	fn on_session_ending(ending_index: SessionIndex, will_apply_at: SessionIndex)
 		-> Option<(Vec<u64>, Vec<(u64, u64)>)>
 	{
-		if !TEST_SESSION_CHANGED.with(|l| *l.borrow()) {
-			let last_validators = Session::validators();
-			let last_identifications = last_validators.into_iter().map(|v| (v, v)).collect();
-			Some((NEXT_VALIDATORS.with(|l| l.borrow().clone()), last_identifications))
-		} else {
-			None
-		}
+		let pair_with_ids = |vals: &[u64]| vals.iter().map(|&v| (v, v)).collect::<Vec<_>>();
+		<Self as OnSessionEnding<_>>::on_session_ending(ending_index, will_apply_at)
+			.map(|vals| (pair_with_ids(&vals), vals))
+			.map(|(ids, vals)| (vals, ids))
 	}
 }
 
diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs
index e7bb42c64df..9386035e44e 100644
--- a/substrate/srml/staking/src/lib.rs
+++ b/substrate/srml/staking/src/lib.rs
@@ -1355,6 +1355,10 @@ impl<T: Trait> Module<T> {
 			// Set the new validator set in sessions.
 			<CurrentElected<T>>::put(&elected_stashes);
 
+			// In order to keep the property required by `n_session_ending`
+			// that we must return the new validator set even if it's the same as the old,
+			// as long as any underlying economic conditions have changed, we don't attempt
+			// to do any optimization where we compare against the prior set.
 			(slot_stake, Some(elected_stashes))
 		} else {
 			// There were not enough candidates for even our minimal level of functionality.
-- 
GitLab