// Copyright 2017-2018 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 module for runtime; manages the authority set ready for the native code.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
use serde_derive::Serialize;
use rstd::prelude::*;
use parity_codec as codec;
use codec::Encode;
use parity_codec_derive::{Encode, Decode};
use srml_support::{storage, Parameter, decl_storage, decl_module};
use srml_support::storage::StorageValue;
use srml_support::storage::unhashed::StorageVec;
use primitives::traits::{MaybeSerializeDebug, Member};
use substrate_primitives::storage::well_known_keys;
use system::{ensure_signed, ensure_inherent};
use inherents::{
ProvideInherent, InherentData, InherentIdentifier, RuntimeString, MakeFatalError
};
#[cfg(any(feature = "std", test))]
use substrate_primitives::Ed25519AuthorityId;
mod mock;
mod tests;
/// The identifier for consensus inherents.
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"offlrep0";
/// The error type used by this inherent.
pub type InherentError = RuntimeString;
struct AuthorityStorageVec(rstd::marker::PhantomData);
impl StorageVec for AuthorityStorageVec {
type Item = S;
const PREFIX: &'static [u8] = well_known_keys::AUTHORITY_PREFIX;
}
pub type KeyValue = (Vec, Vec);
/// Handling offline validator reports in a generic way.
pub trait OnOfflineReport {
fn handle_report(offline: Offline);
}
impl OnOfflineReport for () {
fn handle_report(_: T) {}
}
/// Describes the offline-reporting extrinsic.
pub trait InherentOfflineReport {
/// The report data type passed to the runtime during block authorship.
type Inherent: codec::Codec + Parameter;
/// Whether an inherent is empty and doesn't need to be included.
fn is_empty(inherent: &Self::Inherent) -> bool;
/// Handle the report.
fn handle_report(report: Self::Inherent);
/// Whether two reports are compatible.
fn check_inherent(contained: &Self::Inherent, expected: &Self::Inherent) -> Result<(), &'static str>;
}
impl InherentOfflineReport for () {
type Inherent = ();
fn is_empty(_inherent: &()) -> bool { true }
fn handle_report(_: ()) { }
fn check_inherent(_: &(), _: &()) -> Result<(), &'static str> {
Err("Explicit reporting not allowed")
}
}
/// A variant of the `OfflineReport` which is useful for instant-finality blocks.
///
/// This assumes blocks are only finalized
pub struct InstantFinalityReportVec(::rstd::marker::PhantomData);
impl>> InherentOfflineReport for InstantFinalityReportVec {
type Inherent = Vec;
fn is_empty(inherent: &Self::Inherent) -> bool { inherent.is_empty() }
fn handle_report(report: Vec) {
T::handle_report(report)
}
fn check_inherent(contained: &Self::Inherent, expected: &Self::Inherent) -> Result<(), &'static str> {
contained.iter().try_for_each(|n|
if !expected.contains(n) {
Err("Node we believe online marked offline")
} else {
Ok(())
}
)
}
}
pub type Log = RawLog<
::SessionKey,
>;
/// A logs in this module.
#[cfg_attr(feature = "std", derive(Serialize, Debug))]
#[derive(Encode, Decode, PartialEq, Eq, Clone)]
pub enum RawLog {
/// Authorities set has been changed. Contains the new set of authorities.
AuthoritiesChange(Vec),
}
impl RawLog {
/// Try to cast the log entry as AuthoritiesChange log entry.
pub fn as_authorities_change(&self) -> Option<&[SessionKey]> {
match *self {
RawLog::AuthoritiesChange(ref item) => Some(item),
}
}
}
// Implementation for tests outside of this crate.
#[cfg(any(feature = "std", test))]
impl From> for primitives::testing::DigestItem where N: Into {
fn from(log: RawLog) -> primitives::testing::DigestItem {
match log {
RawLog::AuthoritiesChange(authorities) =>
primitives::generic::DigestItem::AuthoritiesChange(
authorities.into_iter()
.map(Into::into).collect()),
}
}
}
pub trait Trait: system::Trait {
/// Type for all log entries of this module.
type Log: From> + Into>;
type SessionKey: Parameter + Default + MaybeSerializeDebug;
/// Defines the offline-report type of the trait.
/// Set to `()` if offline-reports aren't needed for this runtime.
type InherentOfflineReport: InherentOfflineReport;
}
decl_storage! {
trait Store for Module as Consensus {
// Authorities set actual at the block execution start. IsSome only if
// the set has been changed.
OriginalAuthorities: Option>;
}
add_extra_genesis {
config(authorities): Vec;
#[serde(with = "substrate_primitives::bytes")]
config(code): Vec;
build(|storage: &mut primitives::StorageOverlay, _: &mut primitives::ChildrenStorageOverlay, config: &GenesisConfig| {
use codec::{Encode, KeyedVec};
let auth_count = config.authorities.len() as u32;
config.authorities.iter().enumerate().for_each(|(i, v)| {
storage.insert((i as u32).to_keyed_vec(well_known_keys::AUTHORITY_PREFIX), v.encode());
});
storage.insert(well_known_keys::AUTHORITY_COUNT.to_vec(), auth_count.encode());
storage.insert(well_known_keys::CODE.to_vec(), config.code.clone());
});
}
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
/// Report some misbehaviour.
fn report_misbehavior(origin, _report: Vec) {
ensure_signed(origin)?;
}
/// Note the previous block's validator missed their opportunity to propose a block.
fn note_offline(origin, offline: ::Inherent) {
ensure_inherent(origin)?;
T::InherentOfflineReport::handle_report(offline);
}
/// Make some on-chain remark.
fn remark(origin, _remark: Vec) {
ensure_signed(origin)?;
}
/// Set the number of pages in the WebAssembly environment's heap.
fn set_heap_pages(pages: u64) {
storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode());
}
/// Set the new code.
pub fn set_code(new: Vec) {
storage::unhashed::put_raw(well_known_keys::CODE, &new);
}
/// Set some items of storage.
fn set_storage(items: Vec) {
for i in &items {
storage::unhashed::put_raw(&i.0, &i.1);
}
}
fn on_finalise() {
if let Some(original_authorities) = >::take() {
let current_authorities = AuthorityStorageVec::::items();
if current_authorities != original_authorities {
Self::deposit_log(RawLog::AuthoritiesChange(current_authorities));
}
}
}
}
}
impl Module {
/// Get the current set of authorities. These are the session keys.
pub fn authorities() -> Vec {
AuthorityStorageVec::::items()
}
/// Set the current set of authorities' session keys.
///
/// Called by `next_session` only.
pub fn set_authorities(authorities: &[T::SessionKey]) {
let current_authorities = AuthorityStorageVec::::items();
if current_authorities != authorities {
Self::save_original_authorities(Some(current_authorities));
AuthorityStorageVec::::set_items(authorities);
}
}
/// Set a single authority by index.
pub fn set_authority(index: u32, key: &T::SessionKey) {
let current_authority = AuthorityStorageVec::::item(index);
if current_authority != *key {
Self::save_original_authorities(None);
AuthorityStorageVec::::set_item(index, key);
}
}
/// Save original authorities set.
fn save_original_authorities(current_authorities: Option>) {
if OriginalAuthorities::::get().is_some() {
// if we have already saved original set before, do not overwrite
return;
}
>::put(current_authorities.unwrap_or_else(||
AuthorityStorageVec::::items()));
}
/// Deposit one of this module's logs.
fn deposit_log(log: Log) {
>::deposit_log(::Log::from(log).into());
}
}
impl ProvideInherent for Module {
type Call = Call;
type Error = MakeFatalError;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent(data: &InherentData) -> Option {
if let Ok(Some(data)) =
data.get_data::<::Inherent>(
&INHERENT_IDENTIFIER
)
{
if ::is_empty(&data) {
None
} else {
Some(Call::note_offline(data))
}
} else {
None
}
}
fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
let offline = match call {
Call::note_offline(ref offline) => offline,
_ => return Ok(()),
};
let expected = data
.get_data::<::Inherent>(&INHERENT_IDENTIFIER)?
.ok_or(RuntimeString::from("No `offline_report` found in the inherent data!"))?;
::check_inherent(
&offline, &expected
).map_err(|e| RuntimeString::from(e).into())
}
}