From b06b022cb3e56f843e3bea5099336d1cbb83abd0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 15 Aug 2018 18:08:21 +0200 Subject: [PATCH 01/16] New slashing mechanism (#554) * Slashing improvements - unstake when balance too low - unstake after N slashes according to val prefs - don't early-terminate session/era unless unstaked - offline grace period before punishment * Fix warning * Cleanups and ensure slash_count decays * Bump authoring version and introduce needed authoring stub * Rename * Fix offline tracker * Fix offline tracker * Renames * Add test * Tests * Tests. --- demo/runtime/src/lib.rs | 2 +- polkadot/consensus/src/offline_tracker.rs | 167 +++++++ polkadot/runtime/src/checked_block.rs | 118 +++++ polkadot/runtime/src/lib.rs | 408 ++++++++++++++++++ polkadot/runtime/src/parachains.rs | 377 ++++++++++++++++ substrate/runtime/contract/src/tests.rs | 2 +- substrate/runtime/council/src/lib.rs | 2 +- substrate/runtime/democracy/src/lib.rs | 2 +- substrate/runtime/executive/src/lib.rs | 2 +- substrate/runtime/primitives/src/traits.rs | 5 +- substrate/runtime/session/src/lib.rs | 62 +-- .../runtime/staking/src/genesis_config.rs | 8 +- substrate/runtime/staking/src/lib.rs | 291 +++++++++---- substrate/runtime/staking/src/mock.rs | 3 +- substrate/runtime/staking/src/tests.rs | 131 ++++++ 15 files changed, 1443 insertions(+), 137 deletions(-) create mode 100644 polkadot/consensus/src/offline_tracker.rs create mode 100644 polkadot/runtime/src/checked_block.rs create mode 100644 polkadot/runtime/src/lib.rs create mode 100644 polkadot/runtime/src/parachains.rs diff --git a/demo/runtime/src/lib.rs b/demo/runtime/src/lib.rs index ebc7c777da0c6..6ee577c8170eb 100644 --- a/demo/runtime/src/lib.rs +++ b/demo/runtime/src/lib.rs @@ -121,7 +121,7 @@ impl Convert for SessionKeyConversion { } impl session::Trait for Concrete { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = SessionKeyConversion; type OnSessionChange = Staking; } diff --git a/polkadot/consensus/src/offline_tracker.rs b/polkadot/consensus/src/offline_tracker.rs new file mode 100644 index 0000000000000..eb6c2480c414a --- /dev/null +++ b/polkadot/consensus/src/offline_tracker.rs @@ -0,0 +1,167 @@ +// 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}; + +struct Observed { + last_round_end: Instant, + offline_since: Instant, +} + +#[derive(Eq, PartialEq)] +enum Activity { + Offline, + StillOffline(Duration), + Online, +} + +impl Observed { + fn new() -> Observed { + let now = Instant::now(); + Observed { + last_round_end: now, + offline_since: now, + } + } + + fn note_round_end(&mut self, now: Instant, was_online: Option) { + self.last_round_end = now; + if let Some(false) = was_online { + self.offline_since = now; + } + } + + /// Returns what we have observed about the online/offline state of the validator. + fn activity(&self) -> Activity { + // can happen if clocks are not monotonic + if self.offline_since > self.last_round_end { return Activity::Online } + if self.offline_since == self.last_round_end { return Activity::Offline } + Activity::StillOffline(self.last_round_end.duration_since(self.offline_since)) + } +} + +/// Tracks offline validators and can issue a report for those offline. +pub struct OfflineTracker { + observed: HashMap, + block_instant: Instant, +} + +impl OfflineTracker { + /// Create a new tracker. + pub fn new() -> Self { + OfflineTracker { observed: HashMap::new(), block_instant: Instant::now() } + } + + /// 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)); + + self.block_instant = Instant::now(); + } + + /// 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); + for (val, obs) in self.observed.iter_mut() { + obs.note_round_end( + self.block_instant, + if val == &validator { + Some(was_online) + } else { + None + } + ) + } + } + + /// 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_known_offline_now(v) { + Some(i as u32) + } else { + None + }) + .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. + self.is_known_offline_now(v) + }) + } + + /// Rwturns true only if we have seen the validator miss the last round. For further + /// rounds where we can't say for sure that they're still offline, we give them the + /// benefit of the doubt. + fn is_known_offline_now(&self, v: &AccountId) -> bool { + self.observed.get(v).map(|o| o.activity() == Activity::Offline).unwrap_or(false) + } +} + +#[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_new_block(&[v, v2, v3]); + tracker.note_round_end(v, true); + tracker.note_round_end(v2, true); + tracker.note_round_end(v3, true); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0u32; 0]); + + tracker.note_new_block(&[v, v2, v3]); + tracker.note_round_end(v, true); + tracker.note_round_end(v2, false); + tracker.note_round_end(v3, true); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![1]); + + tracker.note_new_block(&[v, v2, v3]); + tracker.note_round_end(v, false); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); + + tracker.note_new_block(&[v, v2, v3]); + tracker.note_round_end(v, false); + tracker.note_round_end(v2, true); + tracker.note_round_end(v3, false); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 2]); + + tracker.note_new_block(&[v, v2]); + tracker.note_round_end(v, false); + assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); + } +} diff --git a/polkadot/runtime/src/checked_block.rs b/polkadot/runtime/src/checked_block.rs new file mode 100644 index 0000000000000..6ed0cee17614f --- /dev/null +++ b/polkadot/runtime/src/checked_block.rs @@ -0,0 +1,118 @@ +// Copyright 2017 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 . + +//! Typesafe block interaction. + +use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION, NOTE_MISSED_PROPOSAL_POSITION}; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; +use staking::Call as StakingCall; +use primitives::parachain::CandidateReceipt; + +/// Provides a type-safe wrapper around a structurally valid block. +pub struct CheckedBlock { + inner: Block, + file_line: Option<(&'static str, u32)>, +} + +impl CheckedBlock { + /// Create a new checked block. Fails if the block is not structurally valid. + pub fn new(block: Block) -> Result { + let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(_)) => true, + _ => false, + } + }); + + if !has_timestamp { return Err(block) } + + let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { + !xt.is_signed() && match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(_)) => true, + _ => false, + } + }); + + if !has_heads { return Err(block) } + + Ok(CheckedBlock { + inner: block, + file_line: None, + }) + } + + // Creates a new checked block, asserting that it is valid. + #[doc(hidden)] + pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { + CheckedBlock { + inner: block, + file_line: Some((file, line)), + } + } + + /// Extract the timestamp from the block. + pub fn timestamp(&self) -> ::primitives::Timestamp { + let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Timestamp(TimestampCall::set(x)) => Some(x), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Extract the parachain heads from the block. + pub fn parachain_heads(&self) -> &[CandidateReceipt] { + let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), + _ => None + }); + + match x { + Some(x) => x, + None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), + } + } + + /// Extract the noted offline validator indices (if any) from the block. + pub fn noted_offline(&self) -> &[u32] { + self.inner.extrinsics.get(NOTE_MISSED_PROPOSAL_POSITION as usize).and_then(|xt| match xt.extrinsic.function { + Call::Staking(StakingCall::note_missed_proposal(ref x)) => Some(&x[..]), + _ => None, + }).unwrap_or(&[]) + } + + /// Convert into inner block. + pub fn into_inner(self) -> Block { self.inner } +} + +impl ::std::ops::Deref for CheckedBlock { + type Target = Block; + + fn deref(&self) -> &Block { &self.inner } +} + +/// Assert that a block is structurally valid. May lead to panic in the future +/// in case it isn't. +#[macro_export] +macro_rules! assert_polkadot_block { + ($block: expr) => { + $crate::CheckedBlock::new_unchecked($block, file!(), line!()) + } +} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs new file mode 100644 index 0000000000000..1762b78044dde --- /dev/null +++ b/polkadot/runtime/src/lib.rs @@ -0,0 +1,408 @@ +// Copyright 2017 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 . + +//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "std")] +extern crate serde; + +#[macro_use] +extern crate substrate_runtime_io as runtime_io; + +#[macro_use] +extern crate substrate_runtime_support; + +#[macro_use] +extern crate substrate_runtime_primitives as runtime_primitives; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[cfg(test)] +extern crate substrate_serializer; + +extern crate substrate_primitives; + +#[macro_use] +extern crate substrate_runtime_std as rstd; + +extern crate polkadot_primitives as primitives; +extern crate substrate_codec as codec; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_council as council; +extern crate substrate_runtime_democracy as democracy; +extern crate substrate_runtime_executive as executive; +extern crate substrate_runtime_session as session; +extern crate substrate_runtime_staking as staking; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_timestamp as timestamp; +#[macro_use] +extern crate substrate_runtime_version as version; + +#[cfg(feature = "std")] +mod checked_block; +mod parachains; +mod utils; + +#[cfg(feature = "std")] +pub use checked_block::CheckedBlock; +pub use utils::{inherent_extrinsics, check_extrinsic}; +pub use staking::address::Address as RawAddress; + +use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; +use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; +use version::RuntimeVersion; + +#[cfg(feature = "std")] +pub use runtime_primitives::BuildStorage; + +pub use consensus::Call as ConsensusCall; +pub use timestamp::Call as TimestampCall; +pub use parachains::Call as ParachainsCall; +pub use primitives::Header; + +/// The position of the timestamp set extrinsic. +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_MISSED_PROPOSAL_POSITION: u32 = 2; + +/// The address format for describing accounts. +pub type Address = staking::Address; +/// Block Id type for this block. +pub type BlockId = generic::BlockId; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type as expected by this runtime. This is not the type that is signed. +pub type Extrinsic = generic::Extrinsic; +/// Extrinsic type that is signed. +pub type BareExtrinsic = generic::Extrinsic; +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// Concrete runtime type used to parameterize the various modules. +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Concrete; + +/// Polkadot runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: ver_str!("polkadot"), + impl_name: ver_str!("parity-polkadot"), + authoring_version: 2, + spec_version: 4, + impl_version: 0, +}; + +impl version::Trait for Concrete { + const VERSION: RuntimeVersion = VERSION; +} + +/// Version module for this concrete runtime. +pub type Version = version::Module; + +impl HasPublicAux for Concrete { + type PublicAux = AccountId; // TODO: Option +} + +impl system::Trait for Concrete { + type Index = Index; + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Digest = generic::Digest; + type AccountId = AccountId; + type Header = Header; +} +/// System module for this concrete runtime. +pub type System = system::Module; + +impl consensus::Trait for Concrete { + type PublicAux = ::PublicAux; + type SessionKey = SessionKey; +} +/// Consensus module for this concrete runtime. +pub type Consensus = consensus::Module; + +impl timestamp::Trait for Concrete { + const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; + type Moment = u64; +} +/// Timestamp module for this concrete runtime. +pub type Timestamp = timestamp::Module; + +/// Session key conversion. +pub struct SessionKeyConversion; +impl Convert for SessionKeyConversion { + fn convert(a: AccountId) -> SessionKey { + a.0.into() + } +} + +impl session::Trait for Concrete { + const NOTE_MISSED_PROPOSAL_POSITION: u32 = NOTE_MISSED_PROPOSAL_POSITION; + type ConvertAccountIdToSessionKey = SessionKeyConversion; + type OnSessionChange = Staking; +} +/// Session module for this concrete runtime. +pub type Session = session::Module; + +impl staking::Trait for Concrete { + type Balance = Balance; + type AccountIndex = AccountIndex; + type OnAccountKill = (); +} +/// Staking module for this concrete runtime. +pub type Staking = staking::Module; + +impl democracy::Trait for Concrete { + type Proposal = PrivCall; +} +/// Democracy module for this concrete runtime. +pub type Democracy = democracy::Module; + +impl council::Trait for Concrete {} +/// Council module for this concrete runtime. +pub type Council = council::Module; +/// Council voting module for this concrete runtime. +pub type CouncilVoting = council::voting::Module; + +impl parachains::Trait for Concrete { + const SET_POSITION: u32 = PARACHAINS_SET_POSITION; + + type PublicAux = ::PublicAux; +} +pub type Parachains = parachains::Module; + +impl_outer_dispatch! { + /// Call type for polkadot transactions. + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + pub enum Call where aux: ::PublicAux { + Consensus = 0, + Session = 1, + Staking = 2, + Timestamp = 3, + Democracy = 5, + Council = 6, + CouncilVoting = 7, + Parachains = 8, + } + + /// Internal calls. + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + pub enum PrivCall { + Consensus = 0, + Session = 1, + Staking = 2, + Democracy = 5, + Council = 6, + CouncilVoting = 7, + Parachains = 8, + } +} + +/// Executive: handles dispatch to the various modules. +pub type Executive = executive::Executive; + +impl_outer_config! { + pub struct GenesisConfig for Concrete { + ConsensusConfig => consensus, + SystemConfig => system, + SessionConfig => session, + StakingConfig => staking, + DemocracyConfig => democracy, + CouncilConfig => council, + TimestampConfig => timestamp, + ParachainsConfig => parachains, + } +} + +pub mod api { + impl_stubs!( + version => |()| super::Version::version(), + authorities => |()| super::Consensus::authorities(), + initialise_block => |header| super::Executive::initialise_block(&header), + apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), + execute_block => |block| super::Executive::execute_block(block), + finalise_block => |()| super::Executive::finalise_block(), + inherent_extrinsics => |inherent| super::inherent_extrinsics(inherent), + validator_count => |()| super::Session::validator_count(), + validators => |()| super::Session::validators() + ); +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_primitives as primitives; + use codec::{Encode, Decode}; + use substrate_primitives::hexdisplay::HexDisplay; + use substrate_serializer as ser; + use runtime_primitives::traits::{Digest as DigestT, Header as HeaderT}; + type Digest = generic::Digest; + + #[test] + fn test_header_serialization() { + let header = Header { + parent_hash: 5.into(), + number: 67, + state_root: 3.into(), + extrinsics_root: 6.into(), + digest: { let mut d = Digest::default(); d.push(Log(vec![1])); d }, + }; + + assert_eq!(ser::to_string_pretty(&header), r#"{ + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", + "number": 67, + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", + "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006", + "digest": { + "logs": [ + "0x01" + ] + } +}"#); + + let v = header.encode(); + assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); + } + + #[test] + fn block_encoding_round_trip() { + let mut block = Block { + header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), + extrinsics: vec![ + UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Timestamp(timestamp::Call::set(100_000_000)), + signed: Default::default(), + index: Default::default(), + }, + Default::default(), + ) + ], + }; + + let raw = block.encode(); + let decoded = Block::decode(&mut &raw[..]).unwrap(); + + assert_eq!(block, decoded); + + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Staking(staking::Call::stake()), + signed: Default::default(), + index: 10101, + }, + Default::default(), + )); + + let raw = block.encode(); + let decoded = Block::decode(&mut &raw[..]).unwrap(); + + assert_eq!(block, decoded); + } + + #[test] + fn block_encoding_substrate_round_trip() { + let mut block = Block { + header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), + extrinsics: vec![ + UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Timestamp(timestamp::Call::set(100_000_000)), + signed: Default::default(), + index: Default::default(), + }, + Default::default(), + ) + ], + }; + + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Staking(staking::Call::stake()), + signed: Default::default(), + index: 10101, + }, + Default::default() + )); + + let raw = block.encode(); + let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); + let encoded_primitive = decoded_primitive.encode(); + let decoded = Block::decode(&mut &encoded_primitive[..]).unwrap(); + + assert_eq!(block, decoded); + } + + #[test] + fn serialize_unchecked() { + let tx = UncheckedExtrinsic::new( + Extrinsic { + signed: AccountId::from([1; 32]).into(), + index: 999, + function: Call::Timestamp(TimestampCall::set(135135)), + }, + runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() + ); + + // 6f000000 + // ff0101010101010101010101010101010101010101010101010101010101010101 + // e7030000 + // 0300 + // df0f0200 + // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + + let v = Encode::encode(&tx); + assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); + println!("{}", HexDisplay::from(&v)); + assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); + } + + #[test] + fn serialize_checked() { + let xt = Extrinsic { + signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), + index: 0, + function: Call::CouncilVoting(council::voting::Call::propose(Box::new( + PrivCall::Consensus(consensus::PrivCall::set_code( + vec![] + )) + ))), + }; + let v = Encode::encode(&xt); + assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); + } + + #[test] + fn parachain_calls_are_privcall() { + let _register = PrivCall::Parachains(parachains::PrivCall::register_parachain(0.into(), vec![1, 2, 3], vec![])); + let _deregister = PrivCall::Parachains(parachains::PrivCall::deregister_parachain(0.into())); + } +} diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs new file mode 100644 index 0000000000000..8fdb2d01a7e58 --- /dev/null +++ b/polkadot/runtime/src/parachains.rs @@ -0,0 +1,377 @@ +// Copyright 2017 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 . + +//! Main parachains logic. For now this is just the determination of which validators do what. + +use rstd::prelude::*; +use codec::Decode; + +use runtime_primitives::traits::{Hash, BlakeTwo256, Executable, RefInto, MaybeEmpty}; +use primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt}; +use {system, session}; + +use substrate_runtime_support::{StorageValue, StorageMap}; +use substrate_runtime_support::dispatch::Result; + +#[cfg(any(feature = "std", test))] +use rstd::marker::PhantomData; + +#[cfg(any(feature = "std", test))] +use {runtime_io, runtime_primitives}; + +pub trait Trait: system::Trait + session::Trait { + /// The position of the set_heads call in the block. + const SET_POSITION: u32; + + type PublicAux: RefInto + MaybeEmpty; +} + +decl_module! { + /// Parachains module. + pub struct Module; + /// Call type for parachains. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum Call where aux: ::PublicAux { + // provide candidate receipts for parachains, in ascending order by id. + fn set_heads(aux, heads: Vec) -> Result = 0; + } + + /// Private calls for parachains. + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum PrivCall { + fn register_parachain(id: Id, code: Vec, initial_head_data: Vec) -> Result = 0; + fn deregister_parachain(id: Id) -> Result = 1; + } +} + +decl_storage! { + trait Store for Module; + // Vector of all parachain IDs. + pub Parachains get(active_parachains): b"para:chains" => default Vec; + // The parachains registered at present. + pub Code get(parachain_code): b"para:code" => map [ Id => Vec ]; + // The heads of the parachains registered at present. these are kept sorted. + pub Heads get(parachain_head): b"para:head" => map [ Id => Vec ]; + + // Did the parachain heads get updated in this block? + DidUpdate: b"para:did" => default bool; +} + +impl Module { + /// Calculate the current block's duty roster using system's random seed. + pub fn calculate_duty_roster() -> DutyRoster { + let parachains = Self::active_parachains(); + let parachain_count = parachains.len(); + let validator_count = >::validator_count() as usize; + let validators_per_parachain = if parachain_count != 0 { (validator_count - 1) / parachain_count } else { 0 }; + + let mut roles_val = (0..validator_count).map(|i| match i { + i if i < parachain_count * validators_per_parachain => { + let idx = i / validators_per_parachain; + Chain::Parachain(parachains[idx].clone()) + } + _ => Chain::Relay, + }).collect::>(); + + let mut roles_gua = roles_val.clone(); + + let mut random_seed = system::Module::::random_seed().to_vec(); + random_seed.extend(b"validator_role_pairs"); + let mut seed = BlakeTwo256::hash(&random_seed); + + // shuffle + for i in 0..(validator_count - 1) { + // 8 bytes of entropy used per cycle, 32 bytes entropy per hash + let offset = (i * 8 % 32) as usize; + + // number of roles remaining to select from. + let remaining = (validator_count - i) as usize; + + // 4 * 2 32-bit ints per 256-bit seed. + let val_index = u32::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; + let gua_index = u32::decode(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; + + if offset == 24 { + // into the last 8 bytes - rehash to gather new entropy + seed = BlakeTwo256::hash(&seed); + } + + // exchange last item with randomly chosen first. + roles_val.swap(remaining - 1, val_index); + roles_gua.swap(remaining - 1, gua_index); + } + + DutyRoster { + validator_duty: roles_val, + guarantor_duty: roles_gua, + } + } + + /// Register a parachain with given code. + /// Fails if given ID is already used. + pub fn register_parachain(id: Id, code: Vec, initial_head_data: Vec) -> Result { + let mut parachains = Self::active_parachains(); + match parachains.binary_search(&id) { + Ok(_) => fail!("Parachain already exists"), + Err(idx) => parachains.insert(idx, id), + } + + >::insert(id, code); + >::put(parachains); + >::insert(id, initial_head_data); + + Ok(()) + } + + /// Deregister a parachain with given id + pub fn deregister_parachain(id: Id) -> Result { + let mut parachains = Self::active_parachains(); + match parachains.binary_search(&id) { + Ok(idx) => { parachains.remove(idx); } + Err(_) => {} + } + + >::remove(id); + >::remove(id); + >::put(parachains); + Ok(()) + } + + fn set_heads(aux: &::PublicAux, heads: Vec) -> Result { + ensure!(aux.is_empty(), "set_heads must not be signed"); + ensure!(!>::exists(), "Parachain heads must be updated only once in the block"); + ensure!( + >::extrinsic_index() == T::SET_POSITION, + "Parachain heads update extrinsic must be at position {} in the block" +// , T::SET_POSITION + ); + + let active_parachains = Self::active_parachains(); + let mut iter = active_parachains.iter(); + + // perform this check before writing to storage. + for head in &heads { + ensure!( + iter.find(|&p| p == &head.parachain_index).is_some(), + "Submitted candidate for unregistered or out-of-order parachain {}" +// , head.parachain_index.into_inner() + ); + } + + for head in heads { + let id = head.parachain_index.clone(); + >::insert(id, head.head_data.0); + } + + >::put(true); + + Ok(()) + } +} + +impl Executable for Module { + fn execute() { + assert!(::DidUpdate::take(), "Parachain heads must be updated once in the block"); + } +} + +/// Parachains module genesis configuration. +#[cfg(any(feature = "std", test))] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + /// The initial parachains, mapped to code. + pub parachains: Vec<(Id, Vec)>, + /// Phantom data. + #[serde(skip)] + pub phantom: PhantomData, +} + +#[cfg(any(feature = "std", test))] +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + parachains: Vec::new(), + phantom: PhantomData, + } + } +} + +#[cfg(any(feature = "std", test))] +impl runtime_primitives::BuildStorage for GenesisConfig +{ + fn build_storage(mut self) -> ::std::result::Result { + use std::collections::HashMap; + use codec::Encode; + + self.parachains.sort_unstable_by_key(|&(ref id, _)| id.clone()); + self.parachains.dedup_by_key(|&mut (ref id, _)| id.clone()); + + let only_ids: Vec<_> = self.parachains.iter().map(|&(ref id, _)| id).cloned().collect(); + + let mut map: HashMap<_, _> = map![ + Self::hash(>::key()).to_vec() => only_ids.encode() + ]; + + for (id, code) in self.parachains { + let key = Self::hash(&>::key_for(&id)).to_vec(); + map.insert(key, code.encode()); + } + + Ok(map.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use runtime_io::with_externalities; + use substrate_primitives::H256; + use runtime_primitives::BuildStorage; + use runtime_primitives::traits::{HasPublicAux, Identity, BlakeTwo256}; + use runtime_primitives::testing::{Digest, Header}; + use {consensus, timestamp}; + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + impl HasPublicAux for Test { + type PublicAux = u64; + } + impl consensus::Trait for Test { + type PublicAux = ::PublicAux; + type SessionKey = u64; + } + impl system::Trait for Test { + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + } + impl session::Trait for Test { + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; + type ConvertAccountIdToSessionKey = Identity; + type OnSessionChange = (); + } + impl timestamp::Trait for Test { + const TIMESTAMP_SET_POSITION: u32 = 0; + type Moment = u64; + } + impl Trait for Test { + const SET_POSITION: u32 = 0; + + type PublicAux = ::PublicAux; + } + + type Parachains = Module; + + fn new_test_ext(parachains: Vec<(Id, Vec)>) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(consensus::GenesisConfig::{ + code: vec![], + authorities: vec![1, 2, 3], + }.build_storage().unwrap()); + t.extend(session::GenesisConfig::{ + session_length: 1000, + validators: vec![1, 2, 3, 4, 5, 6, 7, 8], + broken_percent_late: 100, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + parachains: parachains, + phantom: PhantomData, + }.build_storage().unwrap()); + t + } + + #[test] + fn active_parachains_should_work() { + let parachains = vec![ + (5u32.into(), vec![1,2,3]), + (100u32.into(), vec![4,5,6]), + ]; + + with_externalities(&mut new_test_ext(parachains), || { + assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]); + assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3])); + assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6])); + }); + } + + #[test] + fn register_deregister() { + let parachains = vec![ + (5u32.into(), vec![1,2,3]), + (100u32.into(), vec![4,5,6]), + ]; + + with_externalities(&mut new_test_ext(parachains), || { + assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]); + + assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3])); + assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6])); + + Parachains::register_parachain(99u32.into(), vec![7,8,9], vec![1, 1, 1]).unwrap(); + + assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]); + assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9])); + + Parachains::deregister_parachain(5u32.into()).unwrap(); + + assert_eq!(Parachains::active_parachains(), vec![99u32.into(), 100u32.into()]); + assert_eq!(Parachains::parachain_code(&5u32.into()), None); + }); + } + + #[test] + fn duty_roster_works() { + let parachains = vec![ + (0u32.into(), vec![]), + (1u32.into(), vec![]), + ]; + + with_externalities(&mut new_test_ext(parachains), || { + let check_roster = |duty_roster: &DutyRoster| { + assert_eq!(duty_roster.validator_duty.len(), 8); + assert_eq!(duty_roster.guarantor_duty.len(), 8); + for i in (0..2).map(Id::from) { + assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); + assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); + } + assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); + assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); + }; + + system::Module::::set_random_seed([0u8; 32].into()); + let duty_roster_0 = Parachains::calculate_duty_roster(); + check_roster(&duty_roster_0); + + system::Module::::set_random_seed([1u8; 32].into()); + let duty_roster_1 = Parachains::calculate_duty_roster(); + check_roster(&duty_roster_1); + assert!(duty_roster_0 != duty_roster_1); + + + system::Module::::set_random_seed([2u8; 32].into()); + let duty_roster_2 = Parachains::calculate_duty_roster(); + check_roster(&duty_roster_2); + assert!(duty_roster_0 != duty_roster_2); + assert!(duty_roster_1 != duty_roster_2); + }); + } +} diff --git a/substrate/runtime/contract/src/tests.rs b/substrate/runtime/contract/src/tests.rs index 53a324907e572..dc5fb885701e1 100644 --- a/substrate/runtime/contract/src/tests.rs +++ b/substrate/runtime/contract/src/tests.rs @@ -55,7 +55,7 @@ impl staking::Trait for Test { type OnAccountKill = Contract; } impl session::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = Staking; } diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index 78be6c1804168..ede3f69b4ead0 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -653,7 +653,7 @@ mod tests { type Header = Header; } impl session::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = staking::Module; } diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index 2c07d5b0d0f99..6489748341ef0 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -395,7 +395,7 @@ mod tests { type Header = Header; } impl session::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = staking::Module; } diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 6910f65ee3f3e..7f65a9311b625 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -252,7 +252,7 @@ mod tests { type Header = Header; } impl session::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = staking::Module; } diff --git a/substrate/runtime/primitives/src/traits.rs b/substrate/runtime/primitives/src/traits.rs index 3c89087d70dc3..7b9fe8568e3d4 100644 --- a/substrate/runtime/primitives/src/traits.rs +++ b/substrate/runtime/primitives/src/traits.rs @@ -26,7 +26,8 @@ use codec::{Codec, Encode}; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{Zero, One, Bounded}; pub use num_traits::ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv}; -use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; +use rstd::ops::{Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, + RemAssign, Shl, Shr}; /// A lazy value. pub trait Lazy { @@ -132,6 +133,7 @@ pub trait SimpleArithmetic: Mul + MulAssign + Div + DivAssign + Rem + RemAssign + + Shl + Shr + CheckedAdd + CheckedSub + CheckedMul + @@ -145,6 +147,7 @@ impl + MulAssign + Div + DivAssign + Rem + RemAssign + + Shl + Shr + CheckedAdd + CheckedSub + CheckedMul + diff --git a/substrate/runtime/session/src/lib.rs b/substrate/runtime/session/src/lib.rs index 59ba338e77a66..fdf780c4df927 100644 --- a/substrate/runtime/session/src/lib.rs +++ b/substrate/runtime/session/src/lib.rs @@ -54,21 +54,21 @@ use runtime_support::dispatch::Result; use std::collections::HashMap; /// A session has changed. -pub trait OnSessionChange { +pub trait OnSessionChange { /// Session has changed. - fn on_session_change(time_elapsed: T, bad_validators: Vec); + fn on_session_change(time_elapsed: T, should_reward: bool); } -impl OnSessionChange for () { - fn on_session_change(_: T, _: Vec) {} +impl OnSessionChange for () { + fn on_session_change(_: T, _: bool) {} } pub trait Trait: timestamp::Trait { // the position of the required timestamp-set extrinsic. - const NOTE_OFFLINE_POSITION: u32; + const NOTE_MISSED_PROPOSAL_POSITION: u32; type ConvertAccountIdToSessionKey: Convert; - type OnSessionChange: OnSessionChange; + type OnSessionChange: OnSessionChange; } decl_module! { @@ -83,7 +83,7 @@ decl_module! { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum PrivCall { fn set_length(new: T::BlockNumber) -> Result = 0; - fn force_new_session(normal_rotation: bool) -> Result = 1; + fn force_new_session(apply_rewards: bool) -> Result = 1; } } @@ -142,8 +142,8 @@ impl Module { } /// Forces a new session. - pub fn force_new_session(normal_rotation: bool) -> Result { - >::put(normal_rotation); + pub fn force_new_session(apply_rewards: bool) -> Result { + >::put(apply_rewards); Ok(()) } @@ -151,9 +151,9 @@ impl Module { pub fn note_offline(aux: &T::PublicAux, offline_val_indices: Vec) -> Result { assert!(aux.is_empty()); assert!( - >::extrinsic_index() == T::NOTE_OFFLINE_POSITION, + >::extrinsic_index() == T::NOTE_MISSED_PROPOSAL_POSITION, "note_offline extrinsic must be at position {} in the block", - T::NOTE_OFFLINE_POSITION + T::NOTE_MISSED_PROPOSAL_POSITION ); let vs = Self::validators(); @@ -181,15 +181,15 @@ impl Module { // check block number and call next_session if necessary. let block_number = >::block_number(); let is_final_block = ((block_number - Self::last_length_change()) % Self::length()).is_zero(); - let bad_validators = >::take().unwrap_or_default(); - let should_end_session = >::take().is_some() || !bad_validators.is_empty() || is_final_block; + let (should_end_session, apply_rewards) = >::take() + .map_or((is_final_block, is_final_block), |apply_rewards| (true, apply_rewards)); if should_end_session { - Self::rotate_session(is_final_block, bad_validators); + Self::rotate_session(is_final_block, apply_rewards); } } /// Move onto next session: register the new authority set. - pub fn rotate_session(is_final_block: bool, bad_validators: Vec) { + pub fn rotate_session(is_final_block: bool, apply_rewards: bool) { let now = >::get(); let time_elapsed = now.clone() - Self::current_start(); @@ -209,7 +209,7 @@ impl Module { >::put(block_number); } - T::OnSessionChange::on_session_change(time_elapsed, bad_validators); + T::OnSessionChange::on_session_change(time_elapsed, apply_rewards); // Update any changes in session keys. Self::validators().iter().enumerate().for_each(|(i, v)| { @@ -314,7 +314,7 @@ mod tests { type Moment = u64; } impl Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = (); } @@ -350,34 +350,6 @@ mod tests { }); } - #[test] - fn should_rotate_on_bad_validators() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(2); - assert_eq!(Session::blocks_remaining(), 0); - Timestamp::set_timestamp(0); - assert_ok!(Session::set_length(3)); - Session::check_rotate_session(); - assert_eq!(Session::current_index(), 1); - assert_eq!(Session::length(), 3); - assert_eq!(Session::current_start(), 0); - assert_eq!(Session::ideal_session_duration(), 15); - // ideal end = 0 + 15 * 3 = 15 - - System::set_block_number(3); - assert_eq!(Session::blocks_remaining(), 2); - Timestamp::set_timestamp(9); // no bad validators. session not rotated. - Session::check_rotate_session(); - - System::set_block_number(4); - ::system::ExtrinsicIndex::::put(1); - assert_eq!(Session::blocks_remaining(), 1); - Session::note_offline(&0, vec![1]).unwrap(); // bad validator -> session rotate - Session::check_rotate_session(); - assert_eq!(Session::current_index(), 2); - }); - } - #[test] fn should_work_with_early_exit() { with_externalities(&mut new_test_ext(), || { diff --git a/substrate/runtime/staking/src/genesis_config.rs b/substrate/runtime/staking/src/genesis_config.rs index af221cc3862ac..eca4867739cf1 100644 --- a/substrate/runtime/staking/src/genesis_config.rs +++ b/substrate/runtime/staking/src/genesis_config.rs @@ -28,7 +28,8 @@ use {runtime_io, primitives}; use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra, BondingDuration, CreationFee, TransferFee, ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalStake, - SessionsPerEra, ValidatorCount, FreeBalance, SessionReward, EarlyEraSlash}; + SessionsPerEra, ValidatorCount, FreeBalance, SessionReward, EarlyEraSlash, + OfflineSlashGrace}; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -48,6 +49,7 @@ pub struct GenesisConfig { pub existential_deposit: T::Balance, pub session_reward: T::Balance, pub early_era_slash: T::Balance, + pub offline_slash_grace: u32, } impl GenesisConfig where T::AccountId: From { @@ -67,6 +69,7 @@ impl GenesisConfig where T::AccountId: From { reclaim_rebate: T::Balance::sa(0), session_reward: T::Balance::sa(0), early_era_slash: T::Balance::sa(0), + offline_slash_grace: 1, } } @@ -94,6 +97,7 @@ impl GenesisConfig where T::AccountId: From { reclaim_rebate: T::Balance::sa(0), session_reward: T::Balance::sa(0), early_era_slash: T::Balance::sa(0), + offline_slash_grace: 1, } } } @@ -115,6 +119,7 @@ impl Default for GenesisConfig { reclaim_rebate: T::Balance::sa(0), session_reward: T::Balance::sa(0), early_era_slash: T::Balance::sa(0), + offline_slash_grace: 0, } } } @@ -138,6 +143,7 @@ impl primitives::BuildStorage for GenesisConfig { Self::hash(>::key()).to_vec() => self.current_era.encode(), Self::hash(>::key()).to_vec() => self.session_reward.encode(), Self::hash(>::key()).to_vec() => self.early_era_slash.encode(), + Self::hash(>::key()).to_vec() => self.offline_slash_grace.encode(), Self::hash(>::key()).to_vec() => total_stake.encode() ]; diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index bb89c2be8b34a..2579168c4cf84 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -52,7 +52,7 @@ use runtime_support::{StorageValue, StorageMap, Parameter}; use runtime_support::dispatch::Result; use session::OnSessionChange; use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, - As, AuxLookup, Member, CheckedAdd, CheckedSub}; + As, AuxLookup, Member, CheckedAdd, CheckedSub, MaybeEmpty}; use address::Address as RawAddress; mod mock; @@ -98,7 +98,40 @@ impl OnAccountKill for () { fn on_account_kill(_who: &AccountId) {} } +/// Preference of what happens on a slash event. +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +#[derive(Eq, PartialEq, Clone, Copy)] +pub struct SlashPreference { + /// Validator should ensure this many more slashes than is necessary before being unstaked. + pub unstake_threshold: u32, +} + +impl Decode for SlashPreference { + fn decode(input: &mut I) -> Option { + Some(SlashPreference { + unstake_threshold: Decode::decode(input)? + }) + } +} + +impl Encode for SlashPreference { + fn encode_to(&self, dest: &mut T) { + self.unstake_threshold.encode_to(dest) + } +} + +impl Default for SlashPreference { + fn default() -> Self { + SlashPreference { + unstake_threshold: 3, + } + } +} + pub trait Trait: system::Trait + session::Trait { + /// The allowed extrinsic position for `missed_proposal` inherent. +// const NOTE_MISSED_PROPOSAL_POSITION: u32; // TODO: uncomment when removed from session::Trait + /// The balance of an account. type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As + As + As; /// Type used for storing an account's index; implies the maximum number of accounts the system @@ -117,9 +150,11 @@ decl_module! { pub enum Call where aux: T::PublicAux { fn transfer(aux, dest: RawAddress, value: T::Balance) -> Result = 0; fn stake(aux) -> Result = 1; - fn unstake(aux, index: u32) -> Result = 2; + fn unstake(aux, intentions_index: u32) -> Result = 2; fn nominate(aux, target: RawAddress) -> Result = 3; fn unnominate(aux, target_index: u32) -> Result = 4; + fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 5; + fn note_missed_proposal(aux, offline_val_indices: Vec) -> Result = 6; } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -127,7 +162,8 @@ decl_module! { fn set_sessions_per_era(new: T::BlockNumber) -> Result = 0; fn set_bonding_duration(new: T::BlockNumber) -> Result = 1; fn set_validator_count(new: u32) -> Result = 2; - fn force_new_era(should_slash: bool) -> Result = 3; + fn force_new_era(apply_rewards: bool) -> Result = 3; + fn set_offline_slash_grace(new: u32) -> Result = 4; } } @@ -159,9 +195,13 @@ decl_storage! { pub SessionReward get(session_reward): b"sta:session_reward" => required T::Balance; // Slash, per validator that is taken per abnormal era end. pub EarlyEraSlash get(early_era_slash): b"sta:early_era_slash" => required T::Balance; + // Number of instances of offline reports before slashing begins for validators. + pub OfflineSlashGrace get(offline_slash_grace): b"sta:offline_slash_grace" => default u32; // The current era index. pub CurrentEra get(current_era): b"sta:era" => required T::BlockNumber; + // Preference over how many times the validator should get slashed for being offline before they are automatically unstaked. + pub SlashPreferenceOf get(slash_preference_of): b"sta:slash_preference_of" => default map [ T::AccountId => SlashPreference ]; // All the accounts with a desire to stake. pub Intentions get(intentions): b"sta:wil:" => default Vec; // All nominator -> nominee relationships. @@ -177,8 +217,8 @@ decl_storage! { // The current era stake threshold pub StakeThreshold get(stake_threshold): b"sta:stake_threshold" => required T::Balance; - // The current bad validator slash. - pub CurrentSlash get(current_slash): b"sta:current_slash" => default T::Balance; + // The number of times a given validator has been reported offline. This gets decremented by one each era that passes. + pub SlashCount get(slash_count): b"sta:slash_count" => default map [ T::AccountId => u32 ]; // The next free enumeration set. pub NextEnumSet get(next_enum_set): b"sta:next_enum" => required T::AccountIndex; @@ -247,6 +287,20 @@ impl Module { Self::free_balance(who) + Self::reserved_balance(who) } + /// Balance of a (potential) validator that includes all nominators. + pub fn nomination_balance(who: &T::AccountId) -> T::Balance { + Self::nominators_for(who).iter() + .map(Self::voting_balance) + .fold(Zero::zero(), |acc, x| acc + x) + } + + /// The total balance that can be slashed from an account. + pub fn slashable_balance(who: &T::AccountId) -> T::Balance { + Self::nominators_for(who).iter() + .map(Self::voting_balance) + .fold(Self::voting_balance(who), |acc, x| acc + x) + } + /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no /// balance changes in the meantime and only the reserved balance is not taken into account. pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { @@ -263,6 +317,22 @@ impl Module { } } + /// Lookup an T::AccountIndex to get an Id, if there's one there. + pub fn lookup_index(index: T::AccountIndex) -> Option { + let enum_set_size = Self::enum_set_size(); + let set = Self::enum_set(index / enum_set_size); + let i: usize = (index % enum_set_size).as_(); + set.get(i).map(|x| x.clone()) + } + + /// `true` if the account `index` is ready for reclaim. + pub fn can_reclaim(try_index: T::AccountIndex) -> bool { + let enum_set_size = Self::enum_set_size(); + let try_set = Self::enum_set(try_index / enum_set_size); + let i = (try_index % enum_set_size).as_(); + i < try_set.len() && Self::voting_balance(&try_set[i]).is_zero() + } + /// The block at which the `who`'s funds become entirely liquid. pub fn unlock_block(who: &T::AccountId) -> LockStatus { match Self::bondage(who) { @@ -328,18 +398,8 @@ impl Module { /// Retract the desire to stake for the transactor. /// /// Effects will be felt at the beginning of the next era. - fn unstake(aux: &T::PublicAux, position: u32) -> Result { - let aux = aux.ref_into(); - let position = position as usize; - let mut intentions = >::get(); -// let position = intentions.iter().position(|t| t == aux.ref_into()).ok_or("Cannot unstake if not already staked.")?; - if intentions.get(position) != Some(aux) { - return Err("Invalid index") - } - intentions.swap_remove(position); - >::put(intentions); - >::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration()); - Ok(()) + fn unstake(aux: &T::PublicAux, intentions_index: u32) -> Result { + Self::apply_unstake(aux.ref_into(), intentions_index as usize) } fn nominate(aux: &T::PublicAux, target: RawAddress) -> Result { @@ -390,6 +450,64 @@ impl Module { Ok(()) } + /// Set the given account's preference for slashing behaviour should they be a validator. + /// + /// An error (no-op) if `Self::intentions()[intentions_index] != aux`. + fn register_slash_preference( + aux: &T::PublicAux, + intentions_index: u32, + p: SlashPreference + ) -> Result { + let aux = aux.ref_into(); + + if Self::intentions().get(intentions_index as usize) != Some(aux) { + return Err("Invalid index") + } + + >::insert(aux, p); + + Ok(()) + } + + /// Note the previous block's validator missed their opportunity to propose a block. This only comes in + /// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant + /// for the previous block. + fn note_missed_proposal(aux: &T::PublicAux, offline_val_indices: Vec) -> Result { + assert!(aux.is_empty()); + assert!( + >::extrinsic_index() == T::NOTE_MISSED_PROPOSAL_POSITION, + "note_missed_proposal extrinsic must be at position {} in the block", + T::NOTE_MISSED_PROPOSAL_POSITION + ); + + for validator_index in offline_val_indices.into_iter() { + let v = >::validators()[validator_index as usize].clone(); + let slash_count = Self::slash_count(&v); + >::insert(v.clone(), slash_count + 1); + let grace = Self::offline_slash_grace(); + + if slash_count >= grace { + let instances = slash_count - grace; + let slash = Self::early_era_slash() << instances; + let next_slash = slash << 1u32; + let _ = Self::slash_validator(&v, slash); + if instances >= Self::slash_preference_of(&v).unstake_threshold + || Self::slashable_balance(&v) < next_slash + { + if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { + Self::apply_unstake(&v, pos) + .expect("pos derived correctly from Self::intentions(); \ + apply_unstake can only fail if pos wrong; \ + Self::intentions() doesn't change; qed"); + } + let _ = Self::force_new_era(false); + } + } + } + + Ok(()) + } + // PRIV DISPATCH /// Set the number of sessions in an era. @@ -412,9 +530,15 @@ impl Module { /// Force there to be a new era. This also forces a new session immediately after by /// setting `normal_rotation` to be false. Validators will get slashed. - fn force_new_era(should_slash: bool) -> Result { + fn force_new_era(apply_rewards: bool) -> Result { >::put(()); - >::force_new_session(!should_slash) + >::force_new_session(apply_rewards) + } + + /// Set the offline slash grace period. + fn set_offline_slash_grace(new: u32) -> Result { + >::put(&new); + Ok(()) } // PUBLIC MUTABLES (DANGEROUS) @@ -598,62 +722,76 @@ impl Module { } } - /// Session has just changed. We need to determine whether we pay a reward, slash and/or - /// move to a new era. - fn new_session(actual_elapsed: T::Moment, bad_validators: Vec) { - let session_index = >::current_index(); - let early_exit_era = !bad_validators.is_empty(); - - if early_exit_era { - // slash - let slash = Self::current_slash() + Self::early_era_slash(); - >::put(&slash); - for v in bad_validators.into_iter() { - if let Some(rem) = Self::slash(&v, slash) { - let noms = Self::current_nominators_for(&v); - let total = noms.iter().map(Self::voting_balance).fold(T::Balance::zero(), |acc, x| acc + x); - if !total.is_zero() { - let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow - for n in noms.iter() { - let _ = Self::slash(n, safe_mul_rational(Self::voting_balance(n))); // best effort - not much that can be done on fail. - } - } + /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, + /// and reduces the nominators' balance if needed. + fn slash_validator(v: &T::AccountId, slash: T::Balance) { + if let Some(rem) = Self::slash(v, slash) { + let noms = Self::current_nominators_for(v); + let total = noms.iter().map(Self::voting_balance).fold(T::Balance::zero(), |acc, x| acc + x); + if !total.is_zero() { + let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow + for n in noms.iter() { + let _ = Self::slash(n, safe_mul_rational(Self::voting_balance(n))); // best effort - not much that can be done on fail. } } - } else { - // Zero any cumulative slash since we're healthy now. - >::kill(); + } + } - // reward - let ideal_elapsed = >::ideal_session_duration(); - let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_(); - let reward = Self::session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64); + /// Reward a given validator by a specific amount. Add the reward to their, and their nominators' + /// balance, pro-rata. + fn reward_validator(who: &T::AccountId, reward: T::Balance) { + let noms = Self::current_nominators_for(who); + let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(who), |acc, x| acc + x); + if !total.is_zero() { + let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow + for n in noms.iter() { + let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n))); + } + let _ = Self::reward(who, safe_mul_rational(Self::voting_balance(who))); + } + } + + /// Actually carry out the unstake operation. + /// Assumes `intentions()[intentions_index] == who`. + fn apply_unstake(who: &T::AccountId, intentions_index: usize) -> Result { + let mut intentions = Self::intentions(); + if intentions.get(intentions_index) != Some(who) { + return Err("Invalid index"); + } + intentions.swap_remove(intentions_index); + >::put(intentions); + >::remove(who); + >::remove(who); + >::insert(who, Self::current_era() + Self::bonding_duration()); + Ok(()) + } + + /// Get the reward for the session, assuming it ends with this block. + fn this_session_reward(actual_elapsed: T::Moment) -> T::Balance { + let ideal_elapsed = >::ideal_session_duration(); + let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_(); + Self::session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64) + } + + /// Session has just changed. We need to determine whether we pay a reward, slash and/or + /// move to a new era. + fn new_session(actual_elapsed: T::Moment, should_reward: bool) { + if should_reward { // apply good session reward + let reward = Self::this_session_reward(actual_elapsed); for v in >::validators().iter() { - let noms = Self::current_nominators_for(v); - let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(v), |acc, x| acc + x); - if !total.is_zero() { - let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow - for n in noms.iter() { - let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n))); - } - let _ = Self::reward(v, safe_mul_rational(Self::voting_balance(v))); - } + Self::reward_validator(v, reward); } } + + let session_index = >::current_index(); if >::take().is_some() || ((session_index - Self::last_era_length_change()) % Self::sessions_per_era()).is_zero() - || early_exit_era { Self::new_era(); } } - /// Balance of a (potential) validator that includes all nominators. - fn nomination_balance(who: &T::AccountId) -> T::Balance { - Self::nominators_for(who).iter().map(Self::voting_balance).fold(Zero::zero(), |acc, x| acc + x) - } - /// The era has changed - enact new staking set. /// /// NOTE: This always happens immediately before a session change to ensure that new validators @@ -670,8 +808,6 @@ impl Module { } } - let minimum_allowed = Self::early_era_slash(); - // evaluate desired staking amounts and nominations and optimise to find the best // combination of validators, then use session::internal::set_validators(). // for now, this just orders would-be stakers by their balances and chooses the top-most @@ -679,8 +815,7 @@ impl Module { // TODO: this is not sound. this should be moved to an off-chain solution mechanism. let mut intentions = >::get() .into_iter() - .map(|v| (Self::voting_balance(&v) + Self::nomination_balance(&v), v)) - .filter(|&(b, _)| b >= minimum_allowed) + .map(|v| (Self::slashable_balance(&v), v)) .collect::>(); intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1)); @@ -696,6 +831,10 @@ impl Module { .collect::>(); for v in >::validators().iter() { >::remove(v); + let slash_count = >::take(v); + if slash_count > 1 { + >::insert(v, slash_count - 1); + } } for v in vals.iter() { >::insert(v, Self::nominators_for(v)); @@ -707,22 +846,6 @@ impl Module { T::AccountIndex::sa(ENUM_SET_SIZE) } - /// Lookup an T::AccountIndex to get an Id, if there's one there. - pub fn lookup_index(index: T::AccountIndex) -> Option { - let enum_set_size = Self::enum_set_size(); - let set = Self::enum_set(index / enum_set_size); - let i: usize = (index % enum_set_size).as_(); - set.get(i).map(|x| x.clone()) - } - - /// `true` if the account `index` is ready for reclaim. - pub fn can_reclaim(try_index: T::AccountIndex) -> bool { - let enum_set_size = Self::enum_set_size(); - let try_set = Self::enum_set(try_index / enum_set_size); - let i = (try_index % enum_set_size).as_(); - i < try_set.len() && Self::voting_balance(&try_set[i]).is_zero() - } - /// Register a new account (with existential balance). fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { let enum_set_size = Self::enum_set_size(); @@ -831,9 +954,9 @@ impl Executable for Module { } } -impl OnSessionChange for Module { - fn on_session_change(elapsed: T::Moment, bad_validators: Vec) { - Self::new_session(elapsed, bad_validators); +impl OnSessionChange for Module { + fn on_session_change(elapsed: T::Moment, should_reward: bool) { + Self::new_session(elapsed, should_reward); } } diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs index 2f3024d78d0ff..e0dc6ce4dcb5f 100644 --- a/substrate/runtime/staking/src/mock.rs +++ b/substrate/runtime/staking/src/mock.rs @@ -45,7 +45,7 @@ impl system::Trait for Test { type Header = Header; } impl session::Trait for Test { - const NOTE_OFFLINE_POSITION: u32 = 1; + const NOTE_MISSED_PROPOSAL_POSITION: u32 = 0; type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = Staking; } @@ -98,6 +98,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 reclaim_rebate: 0, session_reward: reward, early_era_slash: if monied { 20 } else { 0 }, + offline_slash_grace: 0, }.build_storage().unwrap()); t.extend(timestamp::GenesisConfig::{ period: 5 diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs index b8e11cd98ffde..a80930089c14d 100644 --- a/substrate/runtime/staking/src/tests.rs +++ b/substrate/runtime/staking/src/tests.rs @@ -22,6 +22,137 @@ use super::*; use runtime_io::with_externalities; use mock::{Session, Staking, System, Timestamp, Test, new_test_ext}; +#[test] +fn note_null_missed_proposal_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 1); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![])); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 1); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_missed_proposal_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Staking::set_free_balance(&10, 70); + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 70); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Staking::free_balance(&10), 50); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_missed_proposal_exponent_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Staking::set_free_balance(&10, 150); + assert_eq!(Staking::offline_slash_grace(), 0); + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 150); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Staking::free_balance(&10), 130); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::slash_count(&10), 2); + assert_eq!(Staking::free_balance(&10), 90); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_missed_proposal_grace_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Staking::set_free_balance(&10, 70); + Staking::set_free_balance(&20, 70); + assert_ok!(Staking::set_offline_slash_grace(1)); + assert_eq!(Staking::offline_slash_grace(), 1); + + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 70); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Staking::slash_count(&20), 0); + assert_eq!(Staking::free_balance(&20), 70); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); + assert_eq!(Staking::slash_count(&10), 2); + assert_eq!(Staking::free_balance(&10), 50); + assert_eq!(Staking::slash_count(&20), 1); + assert_eq!(Staking::free_balance(&20), 70); + assert!(Staking::forcing_new_era().is_none()); + }); +} + +#[test] +fn note_missed_proposal_force_unstake_session_change_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Staking::set_free_balance(&10, 70); + Staking::set_free_balance(&20, 70); + assert_ok!(Staking::stake(&10)); + assert_ok!(Staking::stake(&20)); + assert_ok!(Staking::stake(&1)); + + assert_eq!(Staking::slash_count(&10), 0); + assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Staking::intentions(), vec![10, 20, 1]); + assert_eq!(Session::validators(), vec![10, 20]); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::free_balance(&10), 50); + assert_eq!(Staking::slash_count(&10), 1); + assert_eq!(Staking::intentions(), vec![10, 20, 1]); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + assert_eq!(Staking::intentions(), vec![1, 20]); + assert_eq!(Staking::free_balance(&10), 10); + assert!(Staking::forcing_new_era().is_some()); + }); +} + +#[test] +fn note_missed_proposal_auto_unstake_session_change_should_work() { + with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { + Staking::set_free_balance(&10, 7000); + Staking::set_free_balance(&20, 7000); + assert_ok!(Staking::stake(&10)); + assert_ok!(Staking::stake(&20)); + assert_ok!(Staking::register_slash_preference(&10, 0, SlashPreference { unstake_threshold: 1 })); + + assert_eq!(Staking::intentions(), vec![10, 20]); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); + assert_eq!(Staking::free_balance(&10), 6980); + assert_eq!(Staking::free_balance(&20), 6980); + assert_eq!(Staking::intentions(), vec![10, 20]); + assert!(Staking::forcing_new_era().is_none()); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); + assert_eq!(Staking::free_balance(&10), 6940); + assert_eq!(Staking::free_balance(&20), 6940); + assert_eq!(Staking::intentions(), vec![20]); + assert!(Staking::forcing_new_era().is_some()); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1])); + assert_eq!(Staking::free_balance(&10), 6940); + assert_eq!(Staking::free_balance(&20), 6860); + assert_eq!(Staking::intentions(), vec![20]); + + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1])); + assert_eq!(Staking::free_balance(&10), 6940); + assert_eq!(Staking::free_balance(&20), 6700); + assert_eq!(Staking::intentions(), vec![0u64; 0]); + }); +} + #[test] fn reward_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { From 8d85ecb3c940feb6345cc3efa75fbb3270fe7cfd Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 15 Aug 2018 18:11:07 +0200 Subject: [PATCH 02/16] Remove accidental merge files. --- polkadot/consensus/src/offline_tracker.rs | 167 --------- polkadot/runtime/src/checked_block.rs | 118 ------- polkadot/runtime/src/lib.rs | 408 ---------------------- polkadot/runtime/src/parachains.rs | 377 -------------------- 4 files changed, 1070 deletions(-) delete mode 100644 polkadot/consensus/src/offline_tracker.rs delete mode 100644 polkadot/runtime/src/checked_block.rs delete mode 100644 polkadot/runtime/src/lib.rs delete mode 100644 polkadot/runtime/src/parachains.rs diff --git a/polkadot/consensus/src/offline_tracker.rs b/polkadot/consensus/src/offline_tracker.rs deleted file mode 100644 index eb6c2480c414a..0000000000000 --- a/polkadot/consensus/src/offline_tracker.rs +++ /dev/null @@ -1,167 +0,0 @@ -// 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}; - -struct Observed { - last_round_end: Instant, - offline_since: Instant, -} - -#[derive(Eq, PartialEq)] -enum Activity { - Offline, - StillOffline(Duration), - Online, -} - -impl Observed { - fn new() -> Observed { - let now = Instant::now(); - Observed { - last_round_end: now, - offline_since: now, - } - } - - fn note_round_end(&mut self, now: Instant, was_online: Option) { - self.last_round_end = now; - if let Some(false) = was_online { - self.offline_since = now; - } - } - - /// Returns what we have observed about the online/offline state of the validator. - fn activity(&self) -> Activity { - // can happen if clocks are not monotonic - if self.offline_since > self.last_round_end { return Activity::Online } - if self.offline_since == self.last_round_end { return Activity::Offline } - Activity::StillOffline(self.last_round_end.duration_since(self.offline_since)) - } -} - -/// Tracks offline validators and can issue a report for those offline. -pub struct OfflineTracker { - observed: HashMap, - block_instant: Instant, -} - -impl OfflineTracker { - /// Create a new tracker. - pub fn new() -> Self { - OfflineTracker { observed: HashMap::new(), block_instant: Instant::now() } - } - - /// 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)); - - self.block_instant = Instant::now(); - } - - /// 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); - for (val, obs) in self.observed.iter_mut() { - obs.note_round_end( - self.block_instant, - if val == &validator { - Some(was_online) - } else { - None - } - ) - } - } - - /// 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_known_offline_now(v) { - Some(i as u32) - } else { - None - }) - .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. - self.is_known_offline_now(v) - }) - } - - /// Rwturns true only if we have seen the validator miss the last round. For further - /// rounds where we can't say for sure that they're still offline, we give them the - /// benefit of the doubt. - fn is_known_offline_now(&self, v: &AccountId) -> bool { - self.observed.get(v).map(|o| o.activity() == Activity::Offline).unwrap_or(false) - } -} - -#[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_new_block(&[v, v2, v3]); - tracker.note_round_end(v, true); - tracker.note_round_end(v2, true); - tracker.note_round_end(v3, true); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0u32; 0]); - - tracker.note_new_block(&[v, v2, v3]); - tracker.note_round_end(v, true); - tracker.note_round_end(v2, false); - tracker.note_round_end(v3, true); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![1]); - - tracker.note_new_block(&[v, v2, v3]); - tracker.note_round_end(v, false); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); - - tracker.note_new_block(&[v, v2, v3]); - tracker.note_round_end(v, false); - tracker.note_round_end(v2, true); - tracker.note_round_end(v3, false); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 2]); - - tracker.note_new_block(&[v, v2]); - tracker.note_round_end(v, false); - assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]); - } -} diff --git a/polkadot/runtime/src/checked_block.rs b/polkadot/runtime/src/checked_block.rs deleted file mode 100644 index 6ed0cee17614f..0000000000000 --- a/polkadot/runtime/src/checked_block.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017 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 . - -//! Typesafe block interaction. - -use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION, NOTE_MISSED_PROPOSAL_POSITION}; -use timestamp::Call as TimestampCall; -use parachains::Call as ParachainsCall; -use staking::Call as StakingCall; -use primitives::parachain::CandidateReceipt; - -/// Provides a type-safe wrapper around a structurally valid block. -pub struct CheckedBlock { - inner: Block, - file_line: Option<(&'static str, u32)>, -} - -impl CheckedBlock { - /// Create a new checked block. Fails if the block is not structurally valid. - pub fn new(block: Block) -> Result { - let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(_)) => true, - _ => false, - } - }); - - if !has_timestamp { return Err(block) } - - let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| { - !xt.is_signed() && match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(_)) => true, - _ => false, - } - }); - - if !has_heads { return Err(block) } - - Ok(CheckedBlock { - inner: block, - file_line: None, - }) - } - - // Creates a new checked block, asserting that it is valid. - #[doc(hidden)] - pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self { - CheckedBlock { - inner: block, - file_line: Some((file, line)), - } - } - - /// Extract the timestamp from the block. - pub fn timestamp(&self) -> ::primitives::Timestamp { - let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Timestamp(TimestampCall::set(x)) => Some(x), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Extract the parachain heads from the block. - pub fn parachain_heads(&self) -> &[CandidateReceipt] { - let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]), - _ => None - }); - - match x { - Some(x) => x, - None => panic!("Invalid polkadot block asserted at {:?}", self.file_line), - } - } - - /// Extract the noted offline validator indices (if any) from the block. - pub fn noted_offline(&self) -> &[u32] { - self.inner.extrinsics.get(NOTE_MISSED_PROPOSAL_POSITION as usize).and_then(|xt| match xt.extrinsic.function { - Call::Staking(StakingCall::note_missed_proposal(ref x)) => Some(&x[..]), - _ => None, - }).unwrap_or(&[]) - } - - /// Convert into inner block. - pub fn into_inner(self) -> Block { self.inner } -} - -impl ::std::ops::Deref for CheckedBlock { - type Target = Block; - - fn deref(&self) -> &Block { &self.inner } -} - -/// Assert that a block is structurally valid. May lead to panic in the future -/// in case it isn't. -#[macro_export] -macro_rules! assert_polkadot_block { - ($block: expr) => { - $crate::CheckedBlock::new_unchecked($block, file!(), line!()) - } -} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs deleted file mode 100644 index 1762b78044dde..0000000000000 --- a/polkadot/runtime/src/lib.rs +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2017 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 . - -//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "std")] -extern crate serde; - -#[macro_use] -extern crate substrate_runtime_io as runtime_io; - -#[macro_use] -extern crate substrate_runtime_support; - -#[macro_use] -extern crate substrate_runtime_primitives as runtime_primitives; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -#[cfg(test)] -extern crate substrate_serializer; - -extern crate substrate_primitives; - -#[macro_use] -extern crate substrate_runtime_std as rstd; - -extern crate polkadot_primitives as primitives; -extern crate substrate_codec as codec; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_council as council; -extern crate substrate_runtime_democracy as democracy; -extern crate substrate_runtime_executive as executive; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_staking as staking; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_timestamp as timestamp; -#[macro_use] -extern crate substrate_runtime_version as version; - -#[cfg(feature = "std")] -mod checked_block; -mod parachains; -mod utils; - -#[cfg(feature = "std")] -pub use checked_block::CheckedBlock; -pub use utils::{inherent_extrinsics, check_extrinsic}; -pub use staking::address::Address as RawAddress; - -use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; -use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; -use version::RuntimeVersion; - -#[cfg(feature = "std")] -pub use runtime_primitives::BuildStorage; - -pub use consensus::Call as ConsensusCall; -pub use timestamp::Call as TimestampCall; -pub use parachains::Call as ParachainsCall; -pub use primitives::Header; - -/// The position of the timestamp set extrinsic. -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_MISSED_PROPOSAL_POSITION: u32 = 2; - -/// The address format for describing accounts. -pub type Address = staking::Address; -/// Block Id type for this block. -pub type BlockId = generic::BlockId; -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type as expected by this runtime. This is not the type that is signed. -pub type Extrinsic = generic::Extrinsic; -/// Extrinsic type that is signed. -pub type BareExtrinsic = generic::Extrinsic; -/// Block type as expected by this runtime. -pub type Block = generic::Block; - -/// Concrete runtime type used to parameterize the various modules. -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Concrete; - -/// Polkadot runtime version. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: ver_str!("polkadot"), - impl_name: ver_str!("parity-polkadot"), - authoring_version: 2, - spec_version: 4, - impl_version: 0, -}; - -impl version::Trait for Concrete { - const VERSION: RuntimeVersion = VERSION; -} - -/// Version module for this concrete runtime. -pub type Version = version::Module; - -impl HasPublicAux for Concrete { - type PublicAux = AccountId; // TODO: Option -} - -impl system::Trait for Concrete { - type Index = Index; - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Digest = generic::Digest; - type AccountId = AccountId; - type Header = Header; -} -/// System module for this concrete runtime. -pub type System = system::Module; - -impl consensus::Trait for Concrete { - type PublicAux = ::PublicAux; - type SessionKey = SessionKey; -} -/// Consensus module for this concrete runtime. -pub type Consensus = consensus::Module; - -impl timestamp::Trait for Concrete { - const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; - type Moment = u64; -} -/// Timestamp module for this concrete runtime. -pub type Timestamp = timestamp::Module; - -/// Session key conversion. -pub struct SessionKeyConversion; -impl Convert for SessionKeyConversion { - fn convert(a: AccountId) -> SessionKey { - a.0.into() - } -} - -impl session::Trait for Concrete { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = NOTE_MISSED_PROPOSAL_POSITION; - type ConvertAccountIdToSessionKey = SessionKeyConversion; - type OnSessionChange = Staking; -} -/// Session module for this concrete runtime. -pub type Session = session::Module; - -impl staking::Trait for Concrete { - type Balance = Balance; - type AccountIndex = AccountIndex; - type OnAccountKill = (); -} -/// Staking module for this concrete runtime. -pub type Staking = staking::Module; - -impl democracy::Trait for Concrete { - type Proposal = PrivCall; -} -/// Democracy module for this concrete runtime. -pub type Democracy = democracy::Module; - -impl council::Trait for Concrete {} -/// Council module for this concrete runtime. -pub type Council = council::Module; -/// Council voting module for this concrete runtime. -pub type CouncilVoting = council::voting::Module; - -impl parachains::Trait for Concrete { - const SET_POSITION: u32 = PARACHAINS_SET_POSITION; - - type PublicAux = ::PublicAux; -} -pub type Parachains = parachains::Module; - -impl_outer_dispatch! { - /// Call type for polkadot transactions. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - pub enum Call where aux: ::PublicAux { - Consensus = 0, - Session = 1, - Staking = 2, - Timestamp = 3, - Democracy = 5, - Council = 6, - CouncilVoting = 7, - Parachains = 8, - } - - /// Internal calls. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - pub enum PrivCall { - Consensus = 0, - Session = 1, - Staking = 2, - Democracy = 5, - Council = 6, - CouncilVoting = 7, - Parachains = 8, - } -} - -/// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; - -impl_outer_config! { - pub struct GenesisConfig for Concrete { - ConsensusConfig => consensus, - SystemConfig => system, - SessionConfig => session, - StakingConfig => staking, - DemocracyConfig => democracy, - CouncilConfig => council, - TimestampConfig => timestamp, - ParachainsConfig => parachains, - } -} - -pub mod api { - impl_stubs!( - version => |()| super::Version::version(), - authorities => |()| super::Consensus::authorities(), - initialise_block => |header| super::Executive::initialise_block(&header), - apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), - execute_block => |block| super::Executive::execute_block(block), - finalise_block => |()| super::Executive::finalise_block(), - inherent_extrinsics => |inherent| super::inherent_extrinsics(inherent), - validator_count => |()| super::Session::validator_count(), - validators => |()| super::Session::validators() - ); -} - -#[cfg(test)] -mod tests { - use super::*; - use substrate_primitives as primitives; - use codec::{Encode, Decode}; - use substrate_primitives::hexdisplay::HexDisplay; - use substrate_serializer as ser; - use runtime_primitives::traits::{Digest as DigestT, Header as HeaderT}; - type Digest = generic::Digest; - - #[test] - fn test_header_serialization() { - let header = Header { - parent_hash: 5.into(), - number: 67, - state_root: 3.into(), - extrinsics_root: 6.into(), - digest: { let mut d = Digest::default(); d.push(Log(vec![1])); d }, - }; - - assert_eq!(ser::to_string_pretty(&header), r#"{ - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", - "number": 67, - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", - "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006", - "digest": { - "logs": [ - "0x01" - ] - } -}"#); - - let v = header.encode(); - assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); - } - - #[test] - fn block_encoding_round_trip() { - let mut block = Block { - header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), - extrinsics: vec![ - UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Timestamp(timestamp::Call::set(100_000_000)), - signed: Default::default(), - index: Default::default(), - }, - Default::default(), - ) - ], - }; - - let raw = block.encode(); - let decoded = Block::decode(&mut &raw[..]).unwrap(); - - assert_eq!(block, decoded); - - block.extrinsics.push(UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Staking(staking::Call::stake()), - signed: Default::default(), - index: 10101, - }, - Default::default(), - )); - - let raw = block.encode(); - let decoded = Block::decode(&mut &raw[..]).unwrap(); - - assert_eq!(block, decoded); - } - - #[test] - fn block_encoding_substrate_round_trip() { - let mut block = Block { - header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), - extrinsics: vec![ - UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Timestamp(timestamp::Call::set(100_000_000)), - signed: Default::default(), - index: Default::default(), - }, - Default::default(), - ) - ], - }; - - block.extrinsics.push(UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Staking(staking::Call::stake()), - signed: Default::default(), - index: 10101, - }, - Default::default() - )); - - let raw = block.encode(); - let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); - let encoded_primitive = decoded_primitive.encode(); - let decoded = Block::decode(&mut &encoded_primitive[..]).unwrap(); - - assert_eq!(block, decoded); - } - - #[test] - fn serialize_unchecked() { - let tx = UncheckedExtrinsic::new( - Extrinsic { - signed: AccountId::from([1; 32]).into(), - index: 999, - function: Call::Timestamp(TimestampCall::set(135135)), - }, - runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() - ); - - // 6f000000 - // ff0101010101010101010101010101010101010101010101010101010101010101 - // e7030000 - // 0300 - // df0f0200 - // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - - let v = Encode::encode(&tx); - assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); - println!("{}", HexDisplay::from(&v)); - assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); - } - - #[test] - fn serialize_checked() { - let xt = Extrinsic { - signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), - index: 0, - function: Call::CouncilVoting(council::voting::Call::propose(Box::new( - PrivCall::Consensus(consensus::PrivCall::set_code( - vec![] - )) - ))), - }; - let v = Encode::encode(&xt); - assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); - } - - #[test] - fn parachain_calls_are_privcall() { - let _register = PrivCall::Parachains(parachains::PrivCall::register_parachain(0.into(), vec![1, 2, 3], vec![])); - let _deregister = PrivCall::Parachains(parachains::PrivCall::deregister_parachain(0.into())); - } -} diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs deleted file mode 100644 index 8fdb2d01a7e58..0000000000000 --- a/polkadot/runtime/src/parachains.rs +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2017 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 . - -//! Main parachains logic. For now this is just the determination of which validators do what. - -use rstd::prelude::*; -use codec::Decode; - -use runtime_primitives::traits::{Hash, BlakeTwo256, Executable, RefInto, MaybeEmpty}; -use primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt}; -use {system, session}; - -use substrate_runtime_support::{StorageValue, StorageMap}; -use substrate_runtime_support::dispatch::Result; - -#[cfg(any(feature = "std", test))] -use rstd::marker::PhantomData; - -#[cfg(any(feature = "std", test))] -use {runtime_io, runtime_primitives}; - -pub trait Trait: system::Trait + session::Trait { - /// The position of the set_heads call in the block. - const SET_POSITION: u32; - - type PublicAux: RefInto + MaybeEmpty; -} - -decl_module! { - /// Parachains module. - pub struct Module; - /// Call type for parachains. - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub enum Call where aux: ::PublicAux { - // provide candidate receipts for parachains, in ascending order by id. - fn set_heads(aux, heads: Vec) -> Result = 0; - } - - /// Private calls for parachains. - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - pub enum PrivCall { - fn register_parachain(id: Id, code: Vec, initial_head_data: Vec) -> Result = 0; - fn deregister_parachain(id: Id) -> Result = 1; - } -} - -decl_storage! { - trait Store for Module; - // Vector of all parachain IDs. - pub Parachains get(active_parachains): b"para:chains" => default Vec; - // The parachains registered at present. - pub Code get(parachain_code): b"para:code" => map [ Id => Vec ]; - // The heads of the parachains registered at present. these are kept sorted. - pub Heads get(parachain_head): b"para:head" => map [ Id => Vec ]; - - // Did the parachain heads get updated in this block? - DidUpdate: b"para:did" => default bool; -} - -impl Module { - /// Calculate the current block's duty roster using system's random seed. - pub fn calculate_duty_roster() -> DutyRoster { - let parachains = Self::active_parachains(); - let parachain_count = parachains.len(); - let validator_count = >::validator_count() as usize; - let validators_per_parachain = if parachain_count != 0 { (validator_count - 1) / parachain_count } else { 0 }; - - let mut roles_val = (0..validator_count).map(|i| match i { - i if i < parachain_count * validators_per_parachain => { - let idx = i / validators_per_parachain; - Chain::Parachain(parachains[idx].clone()) - } - _ => Chain::Relay, - }).collect::>(); - - let mut roles_gua = roles_val.clone(); - - let mut random_seed = system::Module::::random_seed().to_vec(); - random_seed.extend(b"validator_role_pairs"); - let mut seed = BlakeTwo256::hash(&random_seed); - - // shuffle - for i in 0..(validator_count - 1) { - // 8 bytes of entropy used per cycle, 32 bytes entropy per hash - let offset = (i * 8 % 32) as usize; - - // number of roles remaining to select from. - let remaining = (validator_count - i) as usize; - - // 4 * 2 32-bit ints per 256-bit seed. - let val_index = u32::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; - let gua_index = u32::decode(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; - - if offset == 24 { - // into the last 8 bytes - rehash to gather new entropy - seed = BlakeTwo256::hash(&seed); - } - - // exchange last item with randomly chosen first. - roles_val.swap(remaining - 1, val_index); - roles_gua.swap(remaining - 1, gua_index); - } - - DutyRoster { - validator_duty: roles_val, - guarantor_duty: roles_gua, - } - } - - /// Register a parachain with given code. - /// Fails if given ID is already used. - pub fn register_parachain(id: Id, code: Vec, initial_head_data: Vec) -> Result { - let mut parachains = Self::active_parachains(); - match parachains.binary_search(&id) { - Ok(_) => fail!("Parachain already exists"), - Err(idx) => parachains.insert(idx, id), - } - - >::insert(id, code); - >::put(parachains); - >::insert(id, initial_head_data); - - Ok(()) - } - - /// Deregister a parachain with given id - pub fn deregister_parachain(id: Id) -> Result { - let mut parachains = Self::active_parachains(); - match parachains.binary_search(&id) { - Ok(idx) => { parachains.remove(idx); } - Err(_) => {} - } - - >::remove(id); - >::remove(id); - >::put(parachains); - Ok(()) - } - - fn set_heads(aux: &::PublicAux, heads: Vec) -> Result { - ensure!(aux.is_empty(), "set_heads must not be signed"); - ensure!(!>::exists(), "Parachain heads must be updated only once in the block"); - ensure!( - >::extrinsic_index() == T::SET_POSITION, - "Parachain heads update extrinsic must be at position {} in the block" -// , T::SET_POSITION - ); - - let active_parachains = Self::active_parachains(); - let mut iter = active_parachains.iter(); - - // perform this check before writing to storage. - for head in &heads { - ensure!( - iter.find(|&p| p == &head.parachain_index).is_some(), - "Submitted candidate for unregistered or out-of-order parachain {}" -// , head.parachain_index.into_inner() - ); - } - - for head in heads { - let id = head.parachain_index.clone(); - >::insert(id, head.head_data.0); - } - - >::put(true); - - Ok(()) - } -} - -impl Executable for Module { - fn execute() { - assert!(::DidUpdate::take(), "Parachain heads must be updated once in the block"); - } -} - -/// Parachains module genesis configuration. -#[cfg(any(feature = "std", test))] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct GenesisConfig { - /// The initial parachains, mapped to code. - pub parachains: Vec<(Id, Vec)>, - /// Phantom data. - #[serde(skip)] - pub phantom: PhantomData, -} - -#[cfg(any(feature = "std", test))] -impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - parachains: Vec::new(), - phantom: PhantomData, - } - } -} - -#[cfg(any(feature = "std", test))] -impl runtime_primitives::BuildStorage for GenesisConfig -{ - fn build_storage(mut self) -> ::std::result::Result { - use std::collections::HashMap; - use codec::Encode; - - self.parachains.sort_unstable_by_key(|&(ref id, _)| id.clone()); - self.parachains.dedup_by_key(|&mut (ref id, _)| id.clone()); - - let only_ids: Vec<_> = self.parachains.iter().map(|&(ref id, _)| id).cloned().collect(); - - let mut map: HashMap<_, _> = map![ - Self::hash(>::key()).to_vec() => only_ids.encode() - ]; - - for (id, code) in self.parachains { - let key = Self::hash(&>::key_for(&id)).to_vec(); - map.insert(key, code.encode()); - } - - Ok(map.into()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use runtime_io::with_externalities; - use substrate_primitives::H256; - use runtime_primitives::BuildStorage; - use runtime_primitives::traits::{HasPublicAux, Identity, BlakeTwo256}; - use runtime_primitives::testing::{Digest, Header}; - use {consensus, timestamp}; - - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - impl HasPublicAux for Test { - type PublicAux = u64; - } - impl consensus::Trait for Test { - type PublicAux = ::PublicAux; - type SessionKey = u64; - } - impl system::Trait for Test { - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - } - impl session::Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = (); - } - impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; - } - impl Trait for Test { - const SET_POSITION: u32 = 0; - - type PublicAux = ::PublicAux; - } - - type Parachains = Module; - - fn new_test_ext(parachains: Vec<(Id, Vec)>) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![1, 2, 3], - }.build_storage().unwrap()); - t.extend(session::GenesisConfig::{ - session_length: 1000, - validators: vec![1, 2, 3, 4, 5, 6, 7, 8], - broken_percent_late: 100, - }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - parachains: parachains, - phantom: PhantomData, - }.build_storage().unwrap()); - t - } - - #[test] - fn active_parachains_should_work() { - let parachains = vec![ - (5u32.into(), vec![1,2,3]), - (100u32.into(), vec![4,5,6]), - ]; - - with_externalities(&mut new_test_ext(parachains), || { - assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]); - assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3])); - assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6])); - }); - } - - #[test] - fn register_deregister() { - let parachains = vec![ - (5u32.into(), vec![1,2,3]), - (100u32.into(), vec![4,5,6]), - ]; - - with_externalities(&mut new_test_ext(parachains), || { - assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]); - - assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3])); - assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6])); - - Parachains::register_parachain(99u32.into(), vec![7,8,9], vec![1, 1, 1]).unwrap(); - - assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]); - assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9])); - - Parachains::deregister_parachain(5u32.into()).unwrap(); - - assert_eq!(Parachains::active_parachains(), vec![99u32.into(), 100u32.into()]); - assert_eq!(Parachains::parachain_code(&5u32.into()), None); - }); - } - - #[test] - fn duty_roster_works() { - let parachains = vec![ - (0u32.into(), vec![]), - (1u32.into(), vec![]), - ]; - - with_externalities(&mut new_test_ext(parachains), || { - let check_roster = |duty_roster: &DutyRoster| { - assert_eq!(duty_roster.validator_duty.len(), 8); - assert_eq!(duty_roster.guarantor_duty.len(), 8); - for i in (0..2).map(Id::from) { - assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); - assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); - } - assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); - assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); - }; - - system::Module::::set_random_seed([0u8; 32].into()); - let duty_roster_0 = Parachains::calculate_duty_roster(); - check_roster(&duty_roster_0); - - system::Module::::set_random_seed([1u8; 32].into()); - let duty_roster_1 = Parachains::calculate_duty_roster(); - check_roster(&duty_roster_1); - assert!(duty_roster_0 != duty_roster_1); - - - system::Module::::set_random_seed([2u8; 32].into()); - let duty_roster_2 = Parachains::calculate_duty_roster(); - check_roster(&duty_roster_2); - assert!(duty_roster_0 != duty_roster_2); - assert!(duty_roster_1 != duty_roster_2); - }); - } -} From 5d12d61fbf01dfe9443e4bf1e1d712014a915c46 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 15 Aug 2018 21:50:39 +0200 Subject: [PATCH 03/16] Version bump, fixes (#572) * Bump version, don't propose invalid blocks * Fix build. * Fixes. * More fixes. * Fix tests. * Fix more tests * More tests fixed --- Cargo.lock | 250 +++++++++++++++ Cargo.toml | 22 ++ demo/cli/src/lib.rs | 1 + demo/executor/src/lib.rs | 7 +- polkadot/api/src/full.rs | 264 +++++++++++++++ polkadot/runtime/src/lib.rs | 408 ++++++++++++++++++++++++ polkadot/runtime/src/utils.rs | 55 ++++ polkadot/service/src/chain_spec.rs | 193 +++++++++++ substrate/cli/Cargo.toml | 4 + substrate/rpc-servers/src/lib.rs | 2 +- substrate/runtime/contract/src/tests.rs | 1 + substrate/runtime/council/src/lib.rs | 1 + substrate/runtime/democracy/src/lib.rs | 21 +- substrate/runtime/executive/src/lib.rs | 8 +- 14 files changed, 1222 insertions(+), 15 deletions(-) create mode 100644 polkadot/api/src/full.rs create mode 100644 polkadot/runtime/src/lib.rs create mode 100644 polkadot/runtime/src/utils.rs create mode 100644 polkadot/service/src/chain_spec.rs diff --git a/Cargo.lock b/Cargo.lock index 21e7829a7bf44..74ffa55c7d9fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,6 +1770,252 @@ version = "0.2.0" source = "git+https://github.com/paritytech/parity-common#22209e14805e5764f7fe81259324d34eab179264" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +<<<<<<< HEAD +======= + "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot" +version = "0.2.5" +dependencies = [ + "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-cli 0.2.2", + "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-api" +version = "0.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-executor 0.1.0", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-executor 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-executive 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-state-machine 0.1.0", +] + +[[package]] +name = "polkadot-cli" +version = "0.2.2" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "polkadot-service 0.2.2", + "polkadot-transaction-pool 0.1.0", + "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-cli 0.2.5", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-extrinsic-pool 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "substrate-rpc 0.1.0", + "substrate-rpc-servers 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-service 0.2.2", + "substrate-state-machine 0.1.0", + "substrate-telemetry 0.2.2", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-collator" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-cli 0.2.2", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-consensus" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-parachain 0.1.0", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "polkadot-statement-table 0.1.0", + "polkadot-transaction-pool 0.1.0", + "rhododendron 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-bft 0.1.0", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-support 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-executor" +version = "0.1.0" +dependencies = [ + "polkadot-runtime 0.1.0", + "substrate-executor 0.1.0", +] + +[[package]] +name = "polkadot-network" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-consensus 0.1.0", + "polkadot-primitives 0.1.0", + "rhododendron 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-bft 0.1.0", + "substrate-codec 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-parachain" +version = "0.1.0" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-primitives" +version = "0.1.0" +dependencies = [ + "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-serializer 0.1.0", +] + +[[package]] +name = "polkadot-runtime" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-mix 0.1.0", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-council 0.1.0", + "substrate-runtime-democracy 0.1.0", + "substrate-runtime-executive 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-staking 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", + "substrate-runtime-version 0.1.0", + "substrate-serializer 0.1.0", +] + +[[package]] +name = "polkadot-service" +version = "0.2.2" +dependencies = [ + "ed25519 0.1.0", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-consensus 0.1.0", + "polkadot-executor 0.1.0", + "polkadot-network 0.1.0", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "polkadot-transaction-pool 0.1.0", + "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-service 0.2.2", + "substrate-telemetry 0.2.2", + "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "polkadot-statement-table" +version = "0.1.0" +dependencies = [ + "polkadot-primitives 0.1.0", + "substrate-codec 0.1.0", + "substrate-primitives 0.1.0", +] + +[[package]] +name = "polkadot-transaction-pool" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "substrate-client 0.1.0", + "substrate-codec 0.1.0", + "substrate-extrinsic-pool 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-primitives 0.1.0", +>>>>>>> 0a0ffacf... Version bump, fixes (#572) ] [[package]] @@ -2358,7 +2604,11 @@ dependencies = [ [[package]] name = "substrate-cli" +<<<<<<< HEAD version = "0.3.0" +======= +version = "0.2.5" +>>>>>>> 0a0ffacf... Version bump, fixes (#572) dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 1757e91ecabee..218ad9e2d1fd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,25 @@ +<<<<<<< HEAD +======= +[[bin]] +name = "polkadot" +path = "polkadot/src/main.rs" + +[package] +name = "polkadot" +version = "0.2.5" +authors = ["Parity Technologies "] +build = "build.rs" + +[dependencies] +error-chain = "0.12" +polkadot-cli = { path = "polkadot/cli" } +futures = "0.1" +ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } + +[build-dependencies] +vergen = "0.1" + +>>>>>>> 0a0ffacf... Version bump, fixes (#572) [workspace] members = [ "substrate/bft", diff --git a/demo/cli/src/lib.rs b/demo/cli/src/lib.rs index 27a317e45fb8e..5734049808c2d 100644 --- a/demo/cli/src/lib.rs +++ b/demo/cli/src/lib.rs @@ -148,6 +148,7 @@ pub fn run(args: I) -> error::Result<()> where bonding_duration: 90, // 90 days per bond. early_era_slash: 10000, session_reward: 100, + offline_slash_grace: 0, }), democracy: Some(DemocracyConfig { launch_period: 120 * 24 * 14, // 2 weeks per public referendum diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 5911b9b48d87d..bce4de089f230 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -208,6 +208,7 @@ mod tests { reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, + offline_slash_grace: 0, }), democracy: Some(Default::default()), council: Some(Default::default()), @@ -250,7 +251,7 @@ mod tests { // Blake // hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(), // Keccak - hex!("c563199c60df7d914262b1775b284870f3a5da2f24b56d2c6288b37c815a6cd9").into(), + hex!("42b56bd84bbaf239903480e071a8e7733e6b25c120d7f28bb5ec6a9ce96d565a").into(), vec![BareExtrinsic { signed: alice(), index: 0, @@ -266,7 +267,7 @@ mod tests { // Blake // hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(), // Keccak - hex!("83f71d5475f63350825b0301de322233d3711a9f3fcfd74050d1534af47a36b3").into(), + hex!("5b282cd7bbbac9acc2393d9618b87b70b875ea5ffdd5f6d60fe5c68fc435775f").into(), vec![ BareExtrinsic { signed: bob(), @@ -289,7 +290,7 @@ mod tests { // Blake // hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(), // Keccak - hex!("06d026c0d687ec583660a6052de6f89acdb24ea964d06be3831c837c3c426966").into(), + hex!("a3d8f40101bd901c69367b46d6b1d682ad306f506242ed96b33850a7d1c5695a").into(), vec![BareExtrinsic { signed: alice(), index: 0, diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs new file mode 100644 index 0000000000000..ca17188eacfad --- /dev/null +++ b/polkadot/api/src/full.rs @@ -0,0 +1,264 @@ +// Copyright 2017 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 . + +//! Strongly typed API for full Polkadot client. + +use client::backend::LocalBackend; +use client::block_builder::BlockBuilder as ClientBlockBuilder; +use client::{Client, LocalCallExecutor}; +use polkadot_executor::Executor as LocalDispatch; +use substrate_executor::NativeExecutor; +use state_machine; + +use runtime::Address; +use runtime_primitives::traits::AuxLookup; +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}; + +// set up the necessary scaffolding to execute a set of calls to the runtime. +// this creates a new block on top of the given ID and initialises it. +macro_rules! with_runtime { + ($client: ident, $at: expr, $exec: expr) => {{ + let parent = $at; + let header = Header { + parent_hash: $client.block_hash_from_id(&parent)? + .ok_or_else(|| ErrorKind::UnknownBlock(format!("{:?}", parent)))?, + number: $client.block_number_from_id(&parent)? + .ok_or_else(|| ErrorKind::UnknownBlock(format!("{:?}", parent)))? + 1, + state_root: Default::default(), + extrinsics_root: Default::default(), + digest: Default::default(), + }; + + $client.state_at(&parent).map_err(Error::from).and_then(|state| { + let mut changes = Default::default(); + let mut ext = state_machine::Ext::new(&mut changes, &state); + + ::substrate_executor::with_native_environment(&mut ext, || { + ::runtime::Executive::initialise_block(&header); + ($exec)() + }).map_err(Into::into) + }) + }} +} + +impl> BlockBuilder for ClientBlockBuilder>, Block> { + fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { + self.push(extrinsic).map_err(Into::into) + } + + /// Bake the block with provided extrinsics. + fn bake(self) -> Result { + ClientBlockBuilder::bake(self).map_err(Into::into) + } +} + +impl> PolkadotApi for Client>, Block> { + type BlockBuilder = ClientBlockBuilder>, Block>; + + fn session_keys(&self, at: &BlockId) -> Result> { + with_runtime!(self, at, ::runtime::Consensus::authorities) + } + + fn validators(&self, at: &BlockId) -> Result> { + with_runtime!(self, at, ::runtime::Session::validators) + } + + fn random_seed(&self, at: &BlockId) -> Result { + with_runtime!(self, at, ::runtime::System::random_seed) + } + + fn duty_roster(&self, at: &BlockId) -> Result { + with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster) + } + + fn timestamp(&self, at: &BlockId) -> Result { + with_runtime!(self, at, ::runtime::Timestamp::get) + } + + fn evaluate_block(&self, at: &BlockId, block: Block) -> Result { + use substrate_executor::error::ErrorKind as ExecErrorKind; + use codec::{Decode, Encode}; + use runtime::Block as RuntimeBlock; + + let encoded = block.encode(); + let runtime_block = match RuntimeBlock::decode(&mut &encoded[..]) { + Some(x) => x, + None => return Ok(false), + }; + + let res = with_runtime!(self, at, || ::runtime::Executive::execute_block(runtime_block)); + match res { + Ok(()) => Ok(true), + Err(err) => match err.kind() { + &ErrorKind::Executor(ExecErrorKind::Runtime) => Ok(false), + _ => Err(err) + } + } + } + + fn index(&self, at: &BlockId, account: AccountId) -> Result { + with_runtime!(self, at, || ::runtime::System::account_nonce(account)) + } + + fn lookup(&self, at: &BlockId, address: Address) -> Result> { + with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok()) + } + + fn active_parachains(&self, at: &BlockId) -> Result> { + with_runtime!(self, at, ::runtime::Parachains::active_parachains) + } + + fn parachain_code(&self, at: &BlockId, parachain: ParaId) -> Result>> { + with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain)) + } + + fn parachain_head(&self, at: &BlockId, parachain: ParaId) -> Result>> { + with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain)) + } + + 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, inherent_data)? { + block_builder.push(inherent)?; + } + + Ok(block_builder) + } + + fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result> { + use codec::{Encode, Decode}; + + let runtime_version = self.runtime_version_at(at)?; + + with_runtime!(self, at, || { + let extrinsics = ::runtime::inherent_extrinsics(inherent_data, runtime_version); + extrinsics.into_iter() + .map(|x| x.encode()) // get encoded representation + .map(|x| Decode::decode(&mut &x[..])) // get byte-vec equivalent to extrinsic + .map(|x| x.expect("UncheckedExtrinsic has encoded representation equivalent to Vec; qed")) + .collect() + }) + } +} + +impl> LocalPolkadotApi for Client>, Block> +{} + +#[cfg(test)] +mod tests { + use super::*; + use keyring::Keyring; + use client::LocalCallExecutor; + use client::in_mem::Backend as InMemory; + use substrate_executor::NativeExecutionDispatch; + use runtime::{GenesisConfig, ConsensusConfig, SessionConfig}; + + fn validators() -> Vec { + vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + ] + } + + fn session_keys() -> Vec { + vec![ + Keyring::One.to_raw_public().into(), + Keyring::Two.to_raw_public().into(), + ] + } + + fn client() -> Client, LocalCallExecutor, NativeExecutor>, Block> { + let genesis_config = GenesisConfig { + consensus: Some(ConsensusConfig { + code: LocalDispatch::native_equivalent().to_vec(), + authorities: session_keys(), + }), + system: None, + session: Some(SessionConfig { + validators: validators(), + session_length: 100, + broken_percent_late: 100, + }), + council: Some(Default::default()), + democracy: Some(Default::default()), + parachains: Some(Default::default()), + staking: Some(Default::default()), + timestamp: Some(Default::default()), + }; + + ::client::new_in_mem(LocalDispatch::with_heap_pages(8, 8), genesis_config).unwrap() + } + + #[test] + fn gets_session_and_validator_keys() { + let client = client(); + let id = BlockId::number(0); + assert_eq!(client.session_keys(&id).unwrap(), session_keys()); + assert_eq!(client.validators(&id).unwrap(), validators()); + } + + #[test] + fn build_block_implicit_succeeds() { + let client = client(); + + let id = BlockId::number(0); + 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); + assert!(block.header.extrinsics_root != Default::default()); + } + + #[test] + fn build_block_with_inherent_succeeds() { + let client = client(); + + let id = BlockId::number(0); + 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 { + block_builder.push(extrinsic).unwrap(); + } + + let block = block_builder.bake().unwrap(); + + assert_eq!(block.header.number, 1); + assert!(block.header.extrinsics_root != Default::default()); + } + + #[test] + fn gets_random_seed_with_genesis() { + let client = client(); + + let id = BlockId::number(0); + assert!(client.random_seed(&id).is_ok()); + } +} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs new file mode 100644 index 0000000000000..2537c5e5fba5b --- /dev/null +++ b/polkadot/runtime/src/lib.rs @@ -0,0 +1,408 @@ +// Copyright 2017 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 . + +//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "std")] +extern crate serde; + +#[macro_use] +extern crate substrate_runtime_io as runtime_io; + +#[macro_use] +extern crate substrate_runtime_support; + +#[macro_use] +extern crate substrate_runtime_primitives as runtime_primitives; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[cfg(test)] +extern crate substrate_serializer; + +extern crate substrate_primitives; + +#[macro_use] +extern crate substrate_runtime_std as rstd; + +extern crate polkadot_primitives as primitives; +extern crate substrate_codec as codec; +extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_council as council; +extern crate substrate_runtime_democracy as democracy; +extern crate substrate_runtime_executive as executive; +extern crate substrate_runtime_session as session; +extern crate substrate_runtime_staking as staking; +extern crate substrate_runtime_system as system; +extern crate substrate_runtime_timestamp as timestamp; +#[macro_use] +extern crate substrate_runtime_version as version; + +#[cfg(feature = "std")] +mod checked_block; +mod parachains; +mod utils; + +#[cfg(feature = "std")] +pub use checked_block::CheckedBlock; +pub use utils::{inherent_extrinsics, check_extrinsic}; +pub use staking::address::Address as RawAddress; + +use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; +use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; +use version::RuntimeVersion; + +#[cfg(feature = "std")] +pub use runtime_primitives::BuildStorage; + +pub use consensus::Call as ConsensusCall; +pub use timestamp::Call as TimestampCall; +pub use parachains::Call as ParachainsCall; +pub use primitives::Header; + +/// The position of the timestamp set extrinsic. +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_MISSED_PROPOSAL_POSITION: u32 = 2; + +/// The address format for describing accounts. +pub type Address = staking::Address; +/// Block Id type for this block. +pub type BlockId = generic::BlockId; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// Extrinsic type as expected by this runtime. This is not the type that is signed. +pub type Extrinsic = generic::Extrinsic; +/// Extrinsic type that is signed. +pub type BareExtrinsic = generic::Extrinsic; +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// Concrete runtime type used to parameterize the various modules. +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] +pub struct Concrete; + +/// Polkadot runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: ver_str!("polkadot"), + impl_name: ver_str!("parity-polkadot"), + authoring_version: 1, + spec_version: 4, + impl_version: 0, +}; + +impl version::Trait for Concrete { + const VERSION: RuntimeVersion = VERSION; +} + +/// Version module for this concrete runtime. +pub type Version = version::Module; + +impl HasPublicAux for Concrete { + type PublicAux = AccountId; // TODO: Option +} + +impl system::Trait for Concrete { + type Index = Index; + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hashing = BlakeTwo256; + type Digest = generic::Digest; + type AccountId = AccountId; + type Header = Header; +} +/// System module for this concrete runtime. +pub type System = system::Module; + +impl consensus::Trait for Concrete { + type PublicAux = ::PublicAux; + type SessionKey = SessionKey; +} +/// Consensus module for this concrete runtime. +pub type Consensus = consensus::Module; + +impl timestamp::Trait for Concrete { + const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; + type Moment = u64; +} +/// Timestamp module for this concrete runtime. +pub type Timestamp = timestamp::Module; + +/// Session key conversion. +pub struct SessionKeyConversion; +impl Convert for SessionKeyConversion { + fn convert(a: AccountId) -> SessionKey { + a.0.into() + } +} + +impl session::Trait for Concrete { + const NOTE_MISSED_PROPOSAL_POSITION: u32 = NOTE_MISSED_PROPOSAL_POSITION; + type ConvertAccountIdToSessionKey = SessionKeyConversion; + type OnSessionChange = Staking; +} +/// Session module for this concrete runtime. +pub type Session = session::Module; + +impl staking::Trait for Concrete { + type Balance = Balance; + type AccountIndex = AccountIndex; + type OnAccountKill = (); +} +/// Staking module for this concrete runtime. +pub type Staking = staking::Module; + +impl democracy::Trait for Concrete { + type Proposal = PrivCall; +} +/// Democracy module for this concrete runtime. +pub type Democracy = democracy::Module; + +impl council::Trait for Concrete {} +/// Council module for this concrete runtime. +pub type Council = council::Module; +/// Council voting module for this concrete runtime. +pub type CouncilVoting = council::voting::Module; + +impl parachains::Trait for Concrete { + const SET_POSITION: u32 = PARACHAINS_SET_POSITION; + + type PublicAux = ::PublicAux; +} +pub type Parachains = parachains::Module; + +impl_outer_dispatch! { + /// Call type for polkadot transactions. + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + pub enum Call where aux: ::PublicAux { + Consensus = 0, + Session = 1, + Staking = 2, + Timestamp = 3, + Democracy = 5, + Council = 6, + CouncilVoting = 7, + Parachains = 8, + } + + /// Internal calls. + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] + pub enum PrivCall { + Consensus = 0, + Session = 1, + Staking = 2, + Democracy = 5, + Council = 6, + CouncilVoting = 7, + Parachains = 8, + } +} + +/// Executive: handles dispatch to the various modules. +pub type Executive = executive::Executive; + +impl_outer_config! { + pub struct GenesisConfig for Concrete { + ConsensusConfig => consensus, + SystemConfig => system, + SessionConfig => session, + StakingConfig => staking, + DemocracyConfig => democracy, + CouncilConfig => council, + TimestampConfig => timestamp, + ParachainsConfig => parachains, + } +} + +pub mod api { + impl_stubs!( + version => |()| super::Version::version(), + authorities => |()| super::Consensus::authorities(), + initialise_block => |header| super::Executive::initialise_block(&header), + apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), + execute_block => |block| super::Executive::execute_block(block), + finalise_block => |()| super::Executive::finalise_block(), + inherent_extrinsics => |(inherent, version)| super::inherent_extrinsics(inherent, version), + validator_count => |()| super::Session::validator_count(), + validators => |()| super::Session::validators() + ); +} + +#[cfg(test)] +mod tests { + use super::*; + use substrate_primitives as primitives; + use codec::{Encode, Decode}; + use substrate_primitives::hexdisplay::HexDisplay; + use substrate_serializer as ser; + use runtime_primitives::traits::{Digest as DigestT, Header as HeaderT}; + type Digest = generic::Digest; + + #[test] + fn test_header_serialization() { + let header = Header { + parent_hash: 5.into(), + number: 67, + state_root: 3.into(), + extrinsics_root: 6.into(), + digest: { let mut d = Digest::default(); d.push(Log(vec![1])); d }, + }; + + assert_eq!(ser::to_string_pretty(&header), r#"{ + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", + "number": 67, + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", + "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006", + "digest": { + "logs": [ + "0x01" + ] + } +}"#); + + let v = header.encode(); + assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); + } + + #[test] + fn block_encoding_round_trip() { + let mut block = Block { + header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), + extrinsics: vec![ + UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Timestamp(timestamp::Call::set(100_000_000)), + signed: Default::default(), + index: Default::default(), + }, + Default::default(), + ) + ], + }; + + let raw = block.encode(); + let decoded = Block::decode(&mut &raw[..]).unwrap(); + + assert_eq!(block, decoded); + + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Staking(staking::Call::stake()), + signed: Default::default(), + index: 10101, + }, + Default::default(), + )); + + let raw = block.encode(); + let decoded = Block::decode(&mut &raw[..]).unwrap(); + + assert_eq!(block, decoded); + } + + #[test] + fn block_encoding_substrate_round_trip() { + let mut block = Block { + header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), + extrinsics: vec![ + UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Timestamp(timestamp::Call::set(100_000_000)), + signed: Default::default(), + index: Default::default(), + }, + Default::default(), + ) + ], + }; + + block.extrinsics.push(UncheckedExtrinsic::new( + generic::Extrinsic { + function: Call::Staking(staking::Call::stake()), + signed: Default::default(), + index: 10101, + }, + Default::default() + )); + + let raw = block.encode(); + let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); + let encoded_primitive = decoded_primitive.encode(); + let decoded = Block::decode(&mut &encoded_primitive[..]).unwrap(); + + assert_eq!(block, decoded); + } + + #[test] + fn serialize_unchecked() { + let tx = UncheckedExtrinsic::new( + Extrinsic { + signed: AccountId::from([1; 32]).into(), + index: 999, + function: Call::Timestamp(TimestampCall::set(135135)), + }, + runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() + ); + + // 6f000000 + // ff0101010101010101010101010101010101010101010101010101010101010101 + // e7030000 + // 0300 + // df0f0200 + // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + + let v = Encode::encode(&tx); + assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); + println!("{}", HexDisplay::from(&v)); + assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); + } + + #[test] + fn serialize_checked() { + let xt = Extrinsic { + signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), + index: 0, + function: Call::CouncilVoting(council::voting::Call::propose(Box::new( + PrivCall::Consensus(consensus::PrivCall::set_code( + vec![] + )) + ))), + }; + let v = Encode::encode(&xt); + assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); + } + + #[test] + fn parachain_calls_are_privcall() { + let _register = PrivCall::Parachains(parachains::PrivCall::register_parachain(0.into(), vec![1, 2, 3], vec![])); + let _deregister = PrivCall::Parachains(parachains::PrivCall::deregister_parachain(0.into())); + } +} diff --git a/polkadot/runtime/src/utils.rs b/polkadot/runtime/src/utils.rs new file mode 100644 index 0000000000000..6532765438dd7 --- /dev/null +++ b/polkadot/runtime/src/utils.rs @@ -0,0 +1,55 @@ +// Copyright 2017 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 . + +//! Utils for block interaction. + +use rstd::prelude::*; +use super::{Call, UncheckedExtrinsic, Extrinsic, Staking}; +use runtime_primitives::traits::{Checkable, AuxLookup}; +use timestamp::Call as TimestampCall; +use parachains::Call as ParachainsCall; +use staking::Call as StakingCall; +use version::RuntimeVersion; + +/// Produces the list of inherent extrinsics. +pub fn inherent_extrinsics(data: ::primitives::InherentData, runtime_version: RuntimeVersion) -> 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() && runtime_version.spec_version == 4 { + inherent.push(make_inherent( + Call::Staking(StakingCall::note_missed_proposal(data.offline_indices)) + )); + } + + inherent +} + +/// Checks an unchecked extrinsic for validity. +pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { + xt.check_with(Staking::lookup).is_ok() +} diff --git a/polkadot/service/src/chain_spec.rs b/polkadot/service/src/chain_spec.rs new file mode 100644 index 0000000000000..407cc80c1f80b --- /dev/null +++ b/polkadot/service/src/chain_spec.rs @@ -0,0 +1,193 @@ +// Copyright 2017 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 . + +//! Polkadot chain configurations. + +use ed25519; +use primitives::AuthorityId; +use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, + SessionConfig, StakingConfig, TimestampConfig}; +use service::ChainSpec; + +const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; + +pub fn poc_1_testnet_config() -> Result, String> { + ChainSpec::from_embedded(include_bytes!("../res/krummelanke.json")) +} + +fn staging_testnet_config_genesis() -> GenesisConfig { + let initial_authorities = vec![ + hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), + hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), + hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), + hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), + ]; + let endowed_accounts = vec![ + hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), // TODO change + authorities: initial_authorities.clone(), + }), + system: None, + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 60, // that's 5 minutes per session. + broken_percent_late: 50, + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + transaction_base_fee: 100, + transaction_byte_fee: 1, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + early_era_slash: 10000, + session_reward: 100, + balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), + validator_count: 12, + sessions_per_era: 12, // 1 hour per era + bonding_duration: 24, // 1 day per bond. + offline_slash_grace: 0, + }), + democracy: Some(DemocracyConfig { + launch_period: 12 * 60 * 24, // 1 day per public referendum + voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum + minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum + }), + council: Some(CouncilConfig { + active_council: vec![], + candidacy_bond: 5000, // 5000 to become a council candidate + voter_bond: 1000, // 1000 down to vote for a candidate + present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. + carry_count: 6, // carry over the 6 runners-up to the next council election + presentation_duration: 12 * 60 * 24, // one day for presenting winners. + approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. + term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. + desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. + inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. + + cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. + voting_period: 12 * 60 * 24, // 1 day voting period for council members. + }), + parachains: Some(Default::default()), + timestamp: Some(TimestampConfig { + period: 5, // 5 second block time. + }), + } +} + +/// Staging testnet config. +pub fn staging_testnet_config() -> ChainSpec { + let boot_nodes = vec![]; + ChainSpec::from_genesis( + "Staging Testnet", + "staging_testnet", + staging_testnet_config_genesis, + boot_nodes, + Some(STAGING_TELEMETRY_URL.into()), + ) +} + +fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { + let endowed_accounts = vec![ + ed25519::Pair::from_seed(b"Alice ").public().0.into(), + ed25519::Pair::from_seed(b"Bob ").public().0.into(), + ed25519::Pair::from_seed(b"Charlie ").public().0.into(), + ed25519::Pair::from_seed(b"Dave ").public().0.into(), + ed25519::Pair::from_seed(b"Eve ").public().0.into(), + ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), + ]; + GenesisConfig { + consensus: Some(ConsensusConfig { + code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), + authorities: initial_authorities.clone(), + }), + system: None, + session: Some(SessionConfig { + validators: initial_authorities.iter().cloned().map(Into::into).collect(), + session_length: 10, + broken_percent_late: 30, + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: initial_authorities.iter().cloned().map(Into::into).collect(), + transaction_base_fee: 1, + transaction_byte_fee: 0, + existential_deposit: 500, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), + validator_count: 2, + sessions_per_era: 5, + bonding_duration: 2, + early_era_slash: 0, + session_reward: 0, + offline_slash_grace: 0, + }), + democracy: Some(DemocracyConfig { + launch_period: 9, + voting_period: 18, + minimum_deposit: 10, + }), + council: Some(CouncilConfig { + active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(), + candidacy_bond: 10, + voter_bond: 2, + present_slash_per_voter: 1, + carry_count: 4, + presentation_duration: 10, + approval_voting_period: 20, + term_duration: 1000000, + desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, + inactive_grace_period: 1, + + cooloff_period: 75, + voting_period: 20, + }), + parachains: Some(Default::default()), + timestamp: Some(TimestampConfig { + period: 5, // 5 second block time. + }), + } +} + +fn development_config_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ]) +} + +/// Development config (single validator Alice) +pub fn development_config() -> ChainSpec { + ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None) +} + +fn local_testnet_genesis() -> GenesisConfig { + testnet_genesis(vec![ + ed25519::Pair::from_seed(b"Alice ").public().into(), + ed25519::Pair::from_seed(b"Bob ").public().into(), + ]) +} + +/// Local testnet config (multivalidator Alice + Bob) +pub fn local_testnet_config() -> ChainSpec { + ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None) +} diff --git a/substrate/cli/Cargo.toml b/substrate/cli/Cargo.toml index 176d59bbe75d2..82cc8c1bb393a 100644 --- a/substrate/cli/Cargo.toml +++ b/substrate/cli/Cargo.toml @@ -1,6 +1,10 @@ [package] name = "substrate-cli" +<<<<<<< HEAD version = "0.3.0" +======= +version = "0.2.5" +>>>>>>> 0a0ffacf... Version bump, fixes (#572) authors = ["Parity Technologies "] description = "Substrate CLI interface." build = "build.rs" diff --git a/substrate/rpc-servers/src/lib.rs b/substrate/rpc-servers/src/lib.rs index e42996ff6cbc2..f4ba9561d2dda 100644 --- a/substrate/rpc-servers/src/lib.rs +++ b/substrate/rpc-servers/src/lib.rs @@ -49,7 +49,7 @@ pub fn rpc_handler( PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, S: apis::state::StateApi, C: apis::chain::ChainApi, - A: apis::author::AuthorApi, + A: apis::author::AuthorApi, Y: apis::system::SystemApi, { let mut io = pubsub::PubSubHandler::default(); diff --git a/substrate/runtime/contract/src/tests.rs b/substrate/runtime/contract/src/tests.rs index dc5fb885701e1..533f29982f4d1 100644 --- a/substrate/runtime/contract/src/tests.rs +++ b/substrate/runtime/contract/src/tests.rs @@ -109,6 +109,7 @@ fn new_test_ext(existential_deposit: u64, gas_price: u64) -> runtime_io::TestExt reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, + offline_slash_grace: 0, }.build_storage() .unwrap(), ); diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index ede3f69b4ead0..e9a97266359b2 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -697,6 +697,7 @@ mod tests { reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, + offline_slash_grace: 0, }.build_storage().unwrap()); t.extend(democracy::GenesisConfig::{ launch_period: 1, diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index 6489748341ef0..3b7ed946f1c65 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -438,6 +438,7 @@ mod tests { reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, + offline_slash_grace: 0, }.build_storage().unwrap()); t.extend(GenesisConfig::{ launch_period: 1, @@ -499,7 +500,7 @@ mod tests { assert_eq!(Democracy::tally(r), (10, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 2); }); @@ -577,19 +578,19 @@ mod tests { System::set_block_number(1); assert_ok!(Democracy::vote(&1, 0, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::bonding_duration(), 4); System::set_block_number(2); assert_ok!(Democracy::vote(&1, 1, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::bonding_duration(), 3); System::set_block_number(3); assert_ok!(Democracy::vote(&1, 2, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::bonding_duration(), 2); }); } @@ -610,7 +611,7 @@ mod tests { assert_eq!(Democracy::tally(r), (10, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 2); }); @@ -625,7 +626,7 @@ mod tests { assert_ok!(Democracy::cancel_referendum(r)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 1); }); @@ -643,7 +644,7 @@ mod tests { assert_eq!(Democracy::tally(r), (0, 10)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 1); }); @@ -664,7 +665,7 @@ mod tests { assert_eq!(Democracy::tally(r), (110, 100)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 2); }); @@ -681,7 +682,7 @@ mod tests { assert_eq!(Democracy::tally(r), (60, 50)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 1); }); @@ -702,7 +703,7 @@ mod tests { assert_eq!(Democracy::tally(r), (100, 50)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, Vec::new()); + Staking::on_session_change(0, true); assert_eq!(Staking::era_length(), 2); }); diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 7f65a9311b625..7239da4c7938b 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -287,6 +287,7 @@ mod tests { reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, + offline_slash_grace: 0, }.build_storage().unwrap()); let xt = primitives::testing::TestXt((1, 0, Call::transfer(2.into(), 69))); let mut t = runtime_io::TestExternalities::from(t); @@ -314,11 +315,16 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, +<<<<<<< HEAD // Blake // state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(), // Keccak state_root: hex!("8fad93b6b9e5251a2e4913598fd0d74a138c0e486eb1133ff8081b429b0c56f2").into(), extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), // REVIEW: I expected this to be wrong with a different hasher? +======= + state_root: hex!("06efddda99014ce420dc903e6c8b7f87a1c96e699fbb43d26dc5f3203ae94ee0").into(), + extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), +>>>>>>> 0a0ffacf... Version bump, fixes (#572) digest: Digest { logs: vec![], }, }, extrinsics: vec![], @@ -351,7 +357,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("8fad93b6b9e5251a2e4913598fd0d74a138c0e486eb1133ff8081b429b0c56f2").into(), + state_root: hex!("06efddda99014ce420dc903e6c8b7f87a1c96e699fbb43d26dc5f3203ae94ee0").into(), extrinsics_root: [0u8; 32].into(), digest: Digest { logs: vec![], }, }, From fd04ad767cedac6efb3b0053801d349582265d36 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 16 Aug 2018 15:01:31 +0200 Subject: [PATCH 04/16] Fix merge --- Cargo.lock | 250 ------------------------- Cargo.toml | 22 --- substrate/cli/Cargo.toml | 4 - substrate/runtime/executive/src/lib.rs | 5 - 4 files changed, 281 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74ffa55c7d9fd..21e7829a7bf44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1770,252 +1770,6 @@ version = "0.2.0" source = "git+https://github.com/paritytech/parity-common#22209e14805e5764f7fe81259324d34eab179264" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -<<<<<<< HEAD -======= - "ethereum-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot" -version = "0.2.5" -dependencies = [ - "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-cli 0.2.2", - "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-api" -version = "0.1.0" -dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-executor 0.1.0", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-executor 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-executive 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-state-machine 0.1.0", -] - -[[package]] -name = "polkadot-cli" -version = "0.2.2" -dependencies = [ - "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "polkadot-service 0.2.2", - "polkadot-transaction-pool 0.1.0", - "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-cli 0.2.5", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-extrinsic-pool 0.1.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "substrate-rpc 0.1.0", - "substrate-rpc-servers 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-service 0.2.2", - "substrate-state-machine 0.1.0", - "substrate-telemetry 0.2.2", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-collator" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-api 0.1.0", - "polkadot-cli 0.2.2", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-consensus" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-api 0.1.0", - "polkadot-parachain 0.1.0", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "polkadot-statement-table 0.1.0", - "polkadot-transaction-pool 0.1.0", - "rhododendron 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-bft 0.1.0", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-support 0.1.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-executor" -version = "0.1.0" -dependencies = [ - "polkadot-runtime 0.1.0", - "substrate-executor 0.1.0", -] - -[[package]] -name = "polkadot-network" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-api 0.1.0", - "polkadot-consensus 0.1.0", - "polkadot-primitives 0.1.0", - "rhododendron 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-bft 0.1.0", - "substrate-codec 0.1.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-parachain" -version = "0.1.0" -dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-primitives" -version = "0.1.0" -dependencies = [ - "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-serializer 0.1.0", -] - -[[package]] -name = "polkadot-runtime" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-primitives 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "safe-mix 0.1.0", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-codec 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-consensus 0.1.0", - "substrate-runtime-council 0.1.0", - "substrate-runtime-democracy 0.1.0", - "substrate-runtime-executive 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", - "substrate-runtime-std 0.1.0", - "substrate-runtime-support 0.1.0", - "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", - "substrate-runtime-version 0.1.0", - "substrate-serializer 0.1.0", -] - -[[package]] -name = "polkadot-service" -version = "0.2.2" -dependencies = [ - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-api 0.1.0", - "polkadot-consensus 0.1.0", - "polkadot-executor 0.1.0", - "polkadot-network 0.1.0", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "polkadot-transaction-pool 0.1.0", - "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-io 0.1.0", - "substrate-service 0.2.2", - "substrate-telemetry 0.2.2", - "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "polkadot-statement-table" -version = "0.1.0" -dependencies = [ - "polkadot-primitives 0.1.0", - "substrate-codec 0.1.0", - "substrate-primitives 0.1.0", -] - -[[package]] -name = "polkadot-transaction-pool" -version = "0.1.0" -dependencies = [ - "ed25519 0.1.0", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-api 0.1.0", - "polkadot-primitives 0.1.0", - "polkadot-runtime 0.1.0", - "substrate-client 0.1.0", - "substrate-codec 0.1.0", - "substrate-extrinsic-pool 0.1.0", - "substrate-keyring 0.1.0", - "substrate-primitives 0.1.0", - "substrate-runtime-primitives 0.1.0", ->>>>>>> 0a0ffacf... Version bump, fixes (#572) ] [[package]] @@ -2604,11 +2358,7 @@ dependencies = [ [[package]] name = "substrate-cli" -<<<<<<< HEAD version = "0.3.0" -======= -version = "0.2.5" ->>>>>>> 0a0ffacf... Version bump, fixes (#572) dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 218ad9e2d1fd8..1757e91ecabee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,3 @@ -<<<<<<< HEAD -======= -[[bin]] -name = "polkadot" -path = "polkadot/src/main.rs" - -[package] -name = "polkadot" -version = "0.2.5" -authors = ["Parity Technologies "] -build = "build.rs" - -[dependencies] -error-chain = "0.12" -polkadot-cli = { path = "polkadot/cli" } -futures = "0.1" -ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } - -[build-dependencies] -vergen = "0.1" - ->>>>>>> 0a0ffacf... Version bump, fixes (#572) [workspace] members = [ "substrate/bft", diff --git a/substrate/cli/Cargo.toml b/substrate/cli/Cargo.toml index 82cc8c1bb393a..176d59bbe75d2 100644 --- a/substrate/cli/Cargo.toml +++ b/substrate/cli/Cargo.toml @@ -1,10 +1,6 @@ [package] name = "substrate-cli" -<<<<<<< HEAD version = "0.3.0" -======= -version = "0.2.5" ->>>>>>> 0a0ffacf... Version bump, fixes (#572) authors = ["Parity Technologies "] description = "Substrate CLI interface." build = "build.rs" diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 7239da4c7938b..0cb1b989199bf 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -315,16 +315,11 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, -<<<<<<< HEAD // Blake // state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(), // Keccak - state_root: hex!("8fad93b6b9e5251a2e4913598fd0d74a138c0e486eb1133ff8081b429b0c56f2").into(), - extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), // REVIEW: I expected this to be wrong with a different hasher? -======= state_root: hex!("06efddda99014ce420dc903e6c8b7f87a1c96e699fbb43d26dc5f3203ae94ee0").into(), extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), ->>>>>>> 0a0ffacf... Version bump, fixes (#572) digest: Digest { logs: vec![], }, }, extrinsics: vec![], From 15315dece80c760f21f848437d877f7e03710d4d Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 16 Aug 2018 17:55:31 +0200 Subject: [PATCH 05/16] Fix accidental merge bug --- substrate/rpc-servers/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/rpc-servers/src/lib.rs b/substrate/rpc-servers/src/lib.rs index f4ba9561d2dda..e42996ff6cbc2 100644 --- a/substrate/rpc-servers/src/lib.rs +++ b/substrate/rpc-servers/src/lib.rs @@ -49,7 +49,7 @@ pub fn rpc_handler( PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, S: apis::state::StateApi, C: apis::chain::ChainApi, - A: apis::author::AuthorApi, + A: apis::author::AuthorApi, Y: apis::system::SystemApi, { let mut io = pubsub::PubSubHandler::default(); From bd579f1bf0cc3e055d02b033d84023cefd29da67 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 16 Aug 2018 18:18:22 +0200 Subject: [PATCH 06/16] Fixes. --- demo/executor/src/lib.rs | 6 +- polkadot/api/src/full.rs | 264 ------------------- polkadot/runtime/src/lib.rs | 408 ----------------------------- polkadot/runtime/src/utils.rs | 55 ---- polkadot/service/src/chain_spec.rs | 193 -------------- 5 files changed, 3 insertions(+), 923 deletions(-) delete mode 100644 polkadot/api/src/full.rs delete mode 100644 polkadot/runtime/src/lib.rs delete mode 100644 polkadot/runtime/src/utils.rs delete mode 100644 polkadot/service/src/chain_spec.rs diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index bce4de089f230..af813a2c9634e 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -251,7 +251,7 @@ mod tests { // Blake // hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(), // Keccak - hex!("42b56bd84bbaf239903480e071a8e7733e6b25c120d7f28bb5ec6a9ce96d565a").into(), + hex!("6be281d6389f65b144f08acac551207f21bfbc136077b3d385b21fac31cb8818").into(), vec![BareExtrinsic { signed: alice(), index: 0, @@ -267,7 +267,7 @@ mod tests { // Blake // hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(), // Keccak - hex!("5b282cd7bbbac9acc2393d9618b87b70b875ea5ffdd5f6d60fe5c68fc435775f").into(), + hex!("0e39bc5df47bf469d563b1da5d8cd492e7a79c4615229e8b62da1829e92abd5f").into(), vec![ BareExtrinsic { signed: bob(), @@ -290,7 +290,7 @@ mod tests { // Blake // hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(), // Keccak - hex!("a3d8f40101bd901c69367b46d6b1d682ad306f506242ed96b33850a7d1c5695a").into(), + hex!("7b6e9f810656d5d34d0034918e029ca370a189d7d69a16215875e82906440b96").into(), vec![BareExtrinsic { signed: alice(), index: 0, diff --git a/polkadot/api/src/full.rs b/polkadot/api/src/full.rs deleted file mode 100644 index ca17188eacfad..0000000000000 --- a/polkadot/api/src/full.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2017 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 . - -//! Strongly typed API for full Polkadot client. - -use client::backend::LocalBackend; -use client::block_builder::BlockBuilder as ClientBlockBuilder; -use client::{Client, LocalCallExecutor}; -use polkadot_executor::Executor as LocalDispatch; -use substrate_executor::NativeExecutor; -use state_machine; - -use runtime::Address; -use runtime_primitives::traits::AuxLookup; -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}; - -// set up the necessary scaffolding to execute a set of calls to the runtime. -// this creates a new block on top of the given ID and initialises it. -macro_rules! with_runtime { - ($client: ident, $at: expr, $exec: expr) => {{ - let parent = $at; - let header = Header { - parent_hash: $client.block_hash_from_id(&parent)? - .ok_or_else(|| ErrorKind::UnknownBlock(format!("{:?}", parent)))?, - number: $client.block_number_from_id(&parent)? - .ok_or_else(|| ErrorKind::UnknownBlock(format!("{:?}", parent)))? + 1, - state_root: Default::default(), - extrinsics_root: Default::default(), - digest: Default::default(), - }; - - $client.state_at(&parent).map_err(Error::from).and_then(|state| { - let mut changes = Default::default(); - let mut ext = state_machine::Ext::new(&mut changes, &state); - - ::substrate_executor::with_native_environment(&mut ext, || { - ::runtime::Executive::initialise_block(&header); - ($exec)() - }).map_err(Into::into) - }) - }} -} - -impl> BlockBuilder for ClientBlockBuilder>, Block> { - fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> { - self.push(extrinsic).map_err(Into::into) - } - - /// Bake the block with provided extrinsics. - fn bake(self) -> Result { - ClientBlockBuilder::bake(self).map_err(Into::into) - } -} - -impl> PolkadotApi for Client>, Block> { - type BlockBuilder = ClientBlockBuilder>, Block>; - - fn session_keys(&self, at: &BlockId) -> Result> { - with_runtime!(self, at, ::runtime::Consensus::authorities) - } - - fn validators(&self, at: &BlockId) -> Result> { - with_runtime!(self, at, ::runtime::Session::validators) - } - - fn random_seed(&self, at: &BlockId) -> Result { - with_runtime!(self, at, ::runtime::System::random_seed) - } - - fn duty_roster(&self, at: &BlockId) -> Result { - with_runtime!(self, at, ::runtime::Parachains::calculate_duty_roster) - } - - fn timestamp(&self, at: &BlockId) -> Result { - with_runtime!(self, at, ::runtime::Timestamp::get) - } - - fn evaluate_block(&self, at: &BlockId, block: Block) -> Result { - use substrate_executor::error::ErrorKind as ExecErrorKind; - use codec::{Decode, Encode}; - use runtime::Block as RuntimeBlock; - - let encoded = block.encode(); - let runtime_block = match RuntimeBlock::decode(&mut &encoded[..]) { - Some(x) => x, - None => return Ok(false), - }; - - let res = with_runtime!(self, at, || ::runtime::Executive::execute_block(runtime_block)); - match res { - Ok(()) => Ok(true), - Err(err) => match err.kind() { - &ErrorKind::Executor(ExecErrorKind::Runtime) => Ok(false), - _ => Err(err) - } - } - } - - fn index(&self, at: &BlockId, account: AccountId) -> Result { - with_runtime!(self, at, || ::runtime::System::account_nonce(account)) - } - - fn lookup(&self, at: &BlockId, address: Address) -> Result> { - with_runtime!(self, at, || <::runtime::Staking as AuxLookup>::lookup(address).ok()) - } - - fn active_parachains(&self, at: &BlockId) -> Result> { - with_runtime!(self, at, ::runtime::Parachains::active_parachains) - } - - fn parachain_code(&self, at: &BlockId, parachain: ParaId) -> Result>> { - with_runtime!(self, at, || ::runtime::Parachains::parachain_code(parachain)) - } - - fn parachain_head(&self, at: &BlockId, parachain: ParaId) -> Result>> { - with_runtime!(self, at, || ::runtime::Parachains::parachain_head(parachain)) - } - - 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, inherent_data)? { - block_builder.push(inherent)?; - } - - Ok(block_builder) - } - - fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result> { - use codec::{Encode, Decode}; - - let runtime_version = self.runtime_version_at(at)?; - - with_runtime!(self, at, || { - let extrinsics = ::runtime::inherent_extrinsics(inherent_data, runtime_version); - extrinsics.into_iter() - .map(|x| x.encode()) // get encoded representation - .map(|x| Decode::decode(&mut &x[..])) // get byte-vec equivalent to extrinsic - .map(|x| x.expect("UncheckedExtrinsic has encoded representation equivalent to Vec; qed")) - .collect() - }) - } -} - -impl> LocalPolkadotApi for Client>, Block> -{} - -#[cfg(test)] -mod tests { - use super::*; - use keyring::Keyring; - use client::LocalCallExecutor; - use client::in_mem::Backend as InMemory; - use substrate_executor::NativeExecutionDispatch; - use runtime::{GenesisConfig, ConsensusConfig, SessionConfig}; - - fn validators() -> Vec { - vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - ] - } - - fn session_keys() -> Vec { - vec![ - Keyring::One.to_raw_public().into(), - Keyring::Two.to_raw_public().into(), - ] - } - - fn client() -> Client, LocalCallExecutor, NativeExecutor>, Block> { - let genesis_config = GenesisConfig { - consensus: Some(ConsensusConfig { - code: LocalDispatch::native_equivalent().to_vec(), - authorities: session_keys(), - }), - system: None, - session: Some(SessionConfig { - validators: validators(), - session_length: 100, - broken_percent_late: 100, - }), - council: Some(Default::default()), - democracy: Some(Default::default()), - parachains: Some(Default::default()), - staking: Some(Default::default()), - timestamp: Some(Default::default()), - }; - - ::client::new_in_mem(LocalDispatch::with_heap_pages(8, 8), genesis_config).unwrap() - } - - #[test] - fn gets_session_and_validator_keys() { - let client = client(); - let id = BlockId::number(0); - assert_eq!(client.session_keys(&id).unwrap(), session_keys()); - assert_eq!(client.validators(&id).unwrap(), validators()); - } - - #[test] - fn build_block_implicit_succeeds() { - let client = client(); - - let id = BlockId::number(0); - 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); - assert!(block.header.extrinsics_root != Default::default()); - } - - #[test] - fn build_block_with_inherent_succeeds() { - let client = client(); - - let id = BlockId::number(0); - 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 { - block_builder.push(extrinsic).unwrap(); - } - - let block = block_builder.bake().unwrap(); - - assert_eq!(block.header.number, 1); - assert!(block.header.extrinsics_root != Default::default()); - } - - #[test] - fn gets_random_seed_with_genesis() { - let client = client(); - - let id = BlockId::number(0); - assert!(client.random_seed(&id).is_ok()); - } -} diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs deleted file mode 100644 index 2537c5e5fba5b..0000000000000 --- a/polkadot/runtime/src/lib.rs +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2017 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 . - -//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "std")] -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "std")] -extern crate serde; - -#[macro_use] -extern crate substrate_runtime_io as runtime_io; - -#[macro_use] -extern crate substrate_runtime_support; - -#[macro_use] -extern crate substrate_runtime_primitives as runtime_primitives; - -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -#[cfg(test)] -extern crate substrate_serializer; - -extern crate substrate_primitives; - -#[macro_use] -extern crate substrate_runtime_std as rstd; - -extern crate polkadot_primitives as primitives; -extern crate substrate_codec as codec; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_council as council; -extern crate substrate_runtime_democracy as democracy; -extern crate substrate_runtime_executive as executive; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_staking as staking; -extern crate substrate_runtime_system as system; -extern crate substrate_runtime_timestamp as timestamp; -#[macro_use] -extern crate substrate_runtime_version as version; - -#[cfg(feature = "std")] -mod checked_block; -mod parachains; -mod utils; - -#[cfg(feature = "std")] -pub use checked_block::CheckedBlock; -pub use utils::{inherent_extrinsics, check_extrinsic}; -pub use staking::address::Address as RawAddress; - -use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature}; -use runtime_primitives::{generic, traits::{HasPublicAux, BlakeTwo256, Convert}}; -use version::RuntimeVersion; - -#[cfg(feature = "std")] -pub use runtime_primitives::BuildStorage; - -pub use consensus::Call as ConsensusCall; -pub use timestamp::Call as TimestampCall; -pub use parachains::Call as ParachainsCall; -pub use primitives::Header; - -/// The position of the timestamp set extrinsic. -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_MISSED_PROPOSAL_POSITION: u32 = 2; - -/// The address format for describing accounts. -pub type Address = staking::Address; -/// Block Id type for this block. -pub type BlockId = generic::BlockId; -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; -/// Extrinsic type as expected by this runtime. This is not the type that is signed. -pub type Extrinsic = generic::Extrinsic; -/// Extrinsic type that is signed. -pub type BareExtrinsic = generic::Extrinsic; -/// Block type as expected by this runtime. -pub type Block = generic::Block; - -/// Concrete runtime type used to parameterize the various modules. -// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct Concrete; - -/// Polkadot runtime version. -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: ver_str!("polkadot"), - impl_name: ver_str!("parity-polkadot"), - authoring_version: 1, - spec_version: 4, - impl_version: 0, -}; - -impl version::Trait for Concrete { - const VERSION: RuntimeVersion = VERSION; -} - -/// Version module for this concrete runtime. -pub type Version = version::Module; - -impl HasPublicAux for Concrete { - type PublicAux = AccountId; // TODO: Option -} - -impl system::Trait for Concrete { - type Index = Index; - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hashing = BlakeTwo256; - type Digest = generic::Digest; - type AccountId = AccountId; - type Header = Header; -} -/// System module for this concrete runtime. -pub type System = system::Module; - -impl consensus::Trait for Concrete { - type PublicAux = ::PublicAux; - type SessionKey = SessionKey; -} -/// Consensus module for this concrete runtime. -pub type Consensus = consensus::Module; - -impl timestamp::Trait for Concrete { - const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION; - type Moment = u64; -} -/// Timestamp module for this concrete runtime. -pub type Timestamp = timestamp::Module; - -/// Session key conversion. -pub struct SessionKeyConversion; -impl Convert for SessionKeyConversion { - fn convert(a: AccountId) -> SessionKey { - a.0.into() - } -} - -impl session::Trait for Concrete { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = NOTE_MISSED_PROPOSAL_POSITION; - type ConvertAccountIdToSessionKey = SessionKeyConversion; - type OnSessionChange = Staking; -} -/// Session module for this concrete runtime. -pub type Session = session::Module; - -impl staking::Trait for Concrete { - type Balance = Balance; - type AccountIndex = AccountIndex; - type OnAccountKill = (); -} -/// Staking module for this concrete runtime. -pub type Staking = staking::Module; - -impl democracy::Trait for Concrete { - type Proposal = PrivCall; -} -/// Democracy module for this concrete runtime. -pub type Democracy = democracy::Module; - -impl council::Trait for Concrete {} -/// Council module for this concrete runtime. -pub type Council = council::Module; -/// Council voting module for this concrete runtime. -pub type CouncilVoting = council::voting::Module; - -impl parachains::Trait for Concrete { - const SET_POSITION: u32 = PARACHAINS_SET_POSITION; - - type PublicAux = ::PublicAux; -} -pub type Parachains = parachains::Module; - -impl_outer_dispatch! { - /// Call type for polkadot transactions. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - pub enum Call where aux: ::PublicAux { - Consensus = 0, - Session = 1, - Staking = 2, - Timestamp = 3, - Democracy = 5, - Council = 6, - CouncilVoting = 7, - Parachains = 8, - } - - /// Internal calls. - #[derive(Clone, PartialEq, Eq)] - #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] - pub enum PrivCall { - Consensus = 0, - Session = 1, - Staking = 2, - Democracy = 5, - Council = 6, - CouncilVoting = 7, - Parachains = 8, - } -} - -/// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; - -impl_outer_config! { - pub struct GenesisConfig for Concrete { - ConsensusConfig => consensus, - SystemConfig => system, - SessionConfig => session, - StakingConfig => staking, - DemocracyConfig => democracy, - CouncilConfig => council, - TimestampConfig => timestamp, - ParachainsConfig => parachains, - } -} - -pub mod api { - impl_stubs!( - version => |()| super::Version::version(), - authorities => |()| super::Consensus::authorities(), - initialise_block => |header| super::Executive::initialise_block(&header), - apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic), - execute_block => |block| super::Executive::execute_block(block), - finalise_block => |()| super::Executive::finalise_block(), - inherent_extrinsics => |(inherent, version)| super::inherent_extrinsics(inherent, version), - validator_count => |()| super::Session::validator_count(), - validators => |()| super::Session::validators() - ); -} - -#[cfg(test)] -mod tests { - use super::*; - use substrate_primitives as primitives; - use codec::{Encode, Decode}; - use substrate_primitives::hexdisplay::HexDisplay; - use substrate_serializer as ser; - use runtime_primitives::traits::{Digest as DigestT, Header as HeaderT}; - type Digest = generic::Digest; - - #[test] - fn test_header_serialization() { - let header = Header { - parent_hash: 5.into(), - number: 67, - state_root: 3.into(), - extrinsics_root: 6.into(), - digest: { let mut d = Digest::default(); d.push(Log(vec![1])); d }, - }; - - assert_eq!(ser::to_string_pretty(&header), r#"{ - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005", - "number": 67, - "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", - "extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006", - "digest": { - "logs": [ - "0x01" - ] - } -}"#); - - let v = header.encode(); - assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); - } - - #[test] - fn block_encoding_round_trip() { - let mut block = Block { - header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), - extrinsics: vec![ - UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Timestamp(timestamp::Call::set(100_000_000)), - signed: Default::default(), - index: Default::default(), - }, - Default::default(), - ) - ], - }; - - let raw = block.encode(); - let decoded = Block::decode(&mut &raw[..]).unwrap(); - - assert_eq!(block, decoded); - - block.extrinsics.push(UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Staking(staking::Call::stake()), - signed: Default::default(), - index: 10101, - }, - Default::default(), - )); - - let raw = block.encode(); - let decoded = Block::decode(&mut &raw[..]).unwrap(); - - assert_eq!(block, decoded); - } - - #[test] - fn block_encoding_substrate_round_trip() { - let mut block = Block { - header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()), - extrinsics: vec![ - UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Timestamp(timestamp::Call::set(100_000_000)), - signed: Default::default(), - index: Default::default(), - }, - Default::default(), - ) - ], - }; - - block.extrinsics.push(UncheckedExtrinsic::new( - generic::Extrinsic { - function: Call::Staking(staking::Call::stake()), - signed: Default::default(), - index: 10101, - }, - Default::default() - )); - - let raw = block.encode(); - let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap(); - let encoded_primitive = decoded_primitive.encode(); - let decoded = Block::decode(&mut &encoded_primitive[..]).unwrap(); - - assert_eq!(block, decoded); - } - - #[test] - fn serialize_unchecked() { - let tx = UncheckedExtrinsic::new( - Extrinsic { - signed: AccountId::from([1; 32]).into(), - index: 999, - function: Call::Timestamp(TimestampCall::set(135135)), - }, - runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into() - ); - - // 6f000000 - // ff0101010101010101010101010101010101010101010101010101010101010101 - // e7030000 - // 0300 - // df0f0200 - // 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - - let v = Encode::encode(&tx); - assert_eq!(&v[..], &hex!["6f000000ff0101010101010101010101010101010101010101010101010101010101010101e70300000300df0f02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"][..]); - println!("{}", HexDisplay::from(&v)); - assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx); - } - - #[test] - fn serialize_checked() { - let xt = Extrinsic { - signed: AccountId::from(hex!["0d71d1a9cad6f2ab773435a7dec1bac019994d05d1dd5eb3108211dcf25c9d1e"]).into(), - index: 0, - function: Call::CouncilVoting(council::voting::Call::propose(Box::new( - PrivCall::Consensus(consensus::PrivCall::set_code( - vec![] - )) - ))), - }; - let v = Encode::encode(&xt); - assert_eq!(Extrinsic::decode(&mut &v[..]).unwrap(), xt); - } - - #[test] - fn parachain_calls_are_privcall() { - let _register = PrivCall::Parachains(parachains::PrivCall::register_parachain(0.into(), vec![1, 2, 3], vec![])); - let _deregister = PrivCall::Parachains(parachains::PrivCall::deregister_parachain(0.into())); - } -} diff --git a/polkadot/runtime/src/utils.rs b/polkadot/runtime/src/utils.rs deleted file mode 100644 index 6532765438dd7..0000000000000 --- a/polkadot/runtime/src/utils.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017 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 . - -//! Utils for block interaction. - -use rstd::prelude::*; -use super::{Call, UncheckedExtrinsic, Extrinsic, Staking}; -use runtime_primitives::traits::{Checkable, AuxLookup}; -use timestamp::Call as TimestampCall; -use parachains::Call as ParachainsCall; -use staking::Call as StakingCall; -use version::RuntimeVersion; - -/// Produces the list of inherent extrinsics. -pub fn inherent_extrinsics(data: ::primitives::InherentData, runtime_version: RuntimeVersion) -> 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() && runtime_version.spec_version == 4 { - inherent.push(make_inherent( - Call::Staking(StakingCall::note_missed_proposal(data.offline_indices)) - )); - } - - inherent -} - -/// Checks an unchecked extrinsic for validity. -pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool { - xt.check_with(Staking::lookup).is_ok() -} diff --git a/polkadot/service/src/chain_spec.rs b/polkadot/service/src/chain_spec.rs deleted file mode 100644 index 407cc80c1f80b..0000000000000 --- a/polkadot/service/src/chain_spec.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2017 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 . - -//! Polkadot chain configurations. - -use ed25519; -use primitives::AuthorityId; -use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, - SessionConfig, StakingConfig, TimestampConfig}; -use service::ChainSpec; - -const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; - -pub fn poc_1_testnet_config() -> Result, String> { - ChainSpec::from_embedded(include_bytes!("../res/krummelanke.json")) -} - -fn staging_testnet_config_genesis() -> GenesisConfig { - let initial_authorities = vec![ - hex!["82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5"].into(), - hex!["4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7"].into(), - hex!["063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5"].into(), - hex!["8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c"].into(), - ]; - let endowed_accounts = vec![ - hex!["f295940fa750df68a686fcf4abd4111c8a9c5a5a5a83c4c8639c451a94a7adfd"].into(), - ]; - GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), // TODO change - authorities: initial_authorities.clone(), - }), - system: None, - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 60, // that's 5 minutes per session. - broken_percent_late: 50, - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - transaction_base_fee: 100, - transaction_byte_fee: 1, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - early_era_slash: 10000, - session_reward: 100, - balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(), - validator_count: 12, - sessions_per_era: 12, // 1 hour per era - bonding_duration: 24, // 1 day per bond. - offline_slash_grace: 0, - }), - democracy: Some(DemocracyConfig { - launch_period: 12 * 60 * 24, // 1 day per public referendum - voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum - minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum - }), - council: Some(CouncilConfig { - active_council: vec![], - candidacy_bond: 5000, // 5000 to become a council candidate - voter_bond: 1000, // 1000 down to vote for a candidate - present_slash_per_voter: 1, // slash by 1 per voter for an invalid presentation. - carry_count: 6, // carry over the 6 runners-up to the next council election - presentation_duration: 12 * 60 * 24, // one day for presenting winners. - approval_voting_period: 12 * 60 * 24 * 2, // two days period between possible council elections. - term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council. - desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit. - inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped. - - cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal. - voting_period: 12 * 60 * 24, // 1 day voting period for council members. - }), - parachains: Some(Default::default()), - timestamp: Some(TimestampConfig { - period: 5, // 5 second block time. - }), - } -} - -/// Staging testnet config. -pub fn staging_testnet_config() -> ChainSpec { - let boot_nodes = vec![]; - ChainSpec::from_genesis( - "Staging Testnet", - "staging_testnet", - staging_testnet_config_genesis, - boot_nodes, - Some(STAGING_TELEMETRY_URL.into()), - ) -} - -fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { - let endowed_accounts = vec![ - ed25519::Pair::from_seed(b"Alice ").public().0.into(), - ed25519::Pair::from_seed(b"Bob ").public().0.into(), - ed25519::Pair::from_seed(b"Charlie ").public().0.into(), - ed25519::Pair::from_seed(b"Dave ").public().0.into(), - ed25519::Pair::from_seed(b"Eve ").public().0.into(), - ed25519::Pair::from_seed(b"Ferdie ").public().0.into(), - ]; - GenesisConfig { - consensus: Some(ConsensusConfig { - code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), - authorities: initial_authorities.clone(), - }), - system: None, - session: Some(SessionConfig { - validators: initial_authorities.iter().cloned().map(Into::into).collect(), - session_length: 10, - broken_percent_late: 30, - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: initial_authorities.iter().cloned().map(Into::into).collect(), - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 500, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, - balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(), - validator_count: 2, - sessions_per_era: 5, - bonding_duration: 2, - early_era_slash: 0, - session_reward: 0, - offline_slash_grace: 0, - }), - democracy: Some(DemocracyConfig { - launch_period: 9, - voting_period: 18, - minimum_deposit: 10, - }), - council: Some(CouncilConfig { - active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(), - candidacy_bond: 10, - voter_bond: 2, - present_slash_per_voter: 1, - carry_count: 4, - presentation_duration: 10, - approval_voting_period: 20, - term_duration: 1000000, - desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32, - inactive_grace_period: 1, - - cooloff_period: 75, - voting_period: 20, - }), - parachains: Some(Default::default()), - timestamp: Some(TimestampConfig { - period: 5, // 5 second block time. - }), - } -} - -fn development_config_genesis() -> GenesisConfig { - testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ]) -} - -/// Development config (single validator Alice) -pub fn development_config() -> ChainSpec { - ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None) -} - -fn local_testnet_genesis() -> GenesisConfig { - testnet_genesis(vec![ - ed25519::Pair::from_seed(b"Alice ").public().into(), - ed25519::Pair::from_seed(b"Bob ").public().into(), - ]) -} - -/// Local testnet config (multivalidator Alice + Bob) -pub fn local_testnet_config() -> ChainSpec { - ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None) -} From 42c58563d98fd4b98fdaf404068f62302e9e8d31 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 22 Aug 2018 19:21:46 +0200 Subject: [PATCH 07/16] Staking failsafes - Don't slash/unstake/change session when too few staking participants - Introduce set_balance PrivCall --- substrate/runtime/staking/src/lib.rs | 35 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index 2579168c4cf84..32a7924e57ae6 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -64,6 +64,8 @@ mod genesis_config; #[cfg(feature = "std")] pub use genesis_config::GenesisConfig; +const MINIMUM_REQUIRED_VALIDATORS: usize = 4; + /// Number of account IDs stored per enum set. const ENUM_SET_SIZE: usize = 64; @@ -164,6 +166,7 @@ decl_module! { fn set_validator_count(new: u32) -> Result = 2; fn force_new_era(apply_rewards: bool) -> Result = 3; fn set_offline_slash_grace(new: u32) -> Result = 4; + fn set_balance(who: RawAddress, free: T::Balance, reserved: T::Balance) -> Result = 5; } } @@ -399,6 +402,10 @@ impl Module { /// /// Effects will be felt at the beginning of the next era. fn unstake(aux: &T::PublicAux, intentions_index: u32) -> Result { + // unstake fails in degenerate case of having too few existing staked parties + if Self::intentions().len() <= MINIMUM_REQUIRED_VALIDATORS { + return Err("cannot unstake when there are too few staked participants") + } Self::apply_unstake(aux.ref_into(), intentions_index as usize) } @@ -541,6 +548,14 @@ impl Module { Ok(()) } + /// Set the balances of a given account. + fn set_balance(who: Address, free: T::Balance, reserved: T::Balance) -> Result { + let who = Self::lookup(who)?; + Self::set_free_balance(&who, free); + Self::set_reserved_balance(&who, reserved); + Ok(()) + } + // PUBLIC MUTABLES (DANGEROUS) /// Set the free balance of an account to some new value. @@ -725,6 +740,12 @@ impl Module { /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, /// and reduces the nominators' balance if needed. fn slash_validator(v: &T::AccountId, slash: T::Balance) { + // skip the slash in degenerate case of having only 4 staking participants despite having a larger + // desired number of validators (validator_count). + if Self::intentions().len() <= MINIMUM_REQUIRED_VALIDATORS { + return + } + if let Some(rem) = Self::slash(v, slash) { let noms = Self::current_nominators_for(v); let total = noms.iter().map(Self::voting_balance).fold(T::Balance::zero(), |acc, x| acc + x); @@ -813,10 +834,16 @@ impl Module { // for now, this just orders would-be stakers by their balances and chooses the top-most // >::get() of them. // TODO: this is not sound. this should be moved to an off-chain solution mechanism. - let mut intentions = >::get() + let mut intentions = Self::intentions() .into_iter() .map(|v| (Self::slashable_balance(&v), v)) .collect::>(); + + // Avoid making new era if it would leave us with fewer than the minimum needed validators + if intentions.len() < MINIMUM_REQUIRED_VALIDATORS { + return + } + intentions.sort_unstable_by(|&(ref b1, _), &(ref b2, _)| b2.cmp(&b1)); >::put( @@ -826,9 +853,9 @@ impl Module { } else { Zero::zero() } ); let vals = &intentions.into_iter() - .map(|(_, v)| v) - .take(>::get() as usize) - .collect::>(); + .map(|(_, v)| v) + .take(>::get() as usize) + .collect::>(); for v in >::validators().iter() { >::remove(v); let slash_count = >::take(v); From dc6de6cb49d9df007814a27e5f1e2edeb176fd60 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 22 Aug 2018 19:33:58 +0200 Subject: [PATCH 08/16] Make minimum validator count dynamic. --- demo/cli/src/lib.rs | 1 + demo/executor/src/lib.rs | 1 + substrate/runtime/contract/src/tests.rs | 1 + substrate/runtime/council/src/lib.rs | 1 + substrate/runtime/democracy/src/lib.rs | 1 + substrate/runtime/executive/src/lib.rs | 1 + substrate/runtime/staking/src/genesis_config.rs | 7 ++++++- substrate/runtime/staking/src/lib.rs | 16 +++++++++++----- substrate/runtime/staking/src/mock.rs | 1 + 9 files changed, 24 insertions(+), 6 deletions(-) diff --git a/demo/cli/src/lib.rs b/demo/cli/src/lib.rs index 5734049808c2d..4014d6ecfe609 100644 --- a/demo/cli/src/lib.rs +++ b/demo/cli/src/lib.rs @@ -144,6 +144,7 @@ pub fn run(args: I) -> error::Result<()> where existential_deposit: 500, balances: vec![(god_key.clone().into(), 1u64 << 63)].into_iter().collect(), validator_count: 12, + minimum_validator_count: 4, sessions_per_era: 24, // 24 hours per era. bonding_duration: 90, // 90 days per bond. early_era_slash: 10000, diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index af813a2c9634e..5bfe7ca176132 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -199,6 +199,7 @@ mod tests { balances: vec![(alice(), 111)], intentions: vec![alice(), bob(), Charlie.to_raw_public().into()], validator_count: 3, + minimum_validator_count: 0, bonding_duration: 0, transaction_base_fee: 1, transaction_byte_fee: 0, diff --git a/substrate/runtime/contract/src/tests.rs b/substrate/runtime/contract/src/tests.rs index 533f29982f4d1..c248df4537f77 100644 --- a/substrate/runtime/contract/src/tests.rs +++ b/substrate/runtime/contract/src/tests.rs @@ -100,6 +100,7 @@ fn new_test_ext(existential_deposit: u64, gas_price: u64) -> runtime_io::TestExt balances: vec![], intentions: vec![], validator_count: 2, + minimum_validator_count: 0, bonding_duration: 0, transaction_base_fee: 0, transaction_byte_fee: 0, diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index e9a97266359b2..0214d97dab570 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -688,6 +688,7 @@ mod tests { balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], intentions: vec![], validator_count: 2, + minimum_validator_count: 0, bonding_duration: 0, transaction_base_fee: 0, transaction_byte_fee: 0, diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index 3b7ed946f1c65..20ad062247495 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -429,6 +429,7 @@ mod tests { balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], intentions: vec![], validator_count: 2, + minimum_validator_count: 0, bonding_duration: 3, transaction_base_fee: 0, transaction_byte_fee: 0, diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 0cb1b989199bf..1e58dcf5713a1 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -278,6 +278,7 @@ mod tests { balances: vec![(1, 111)], intentions: vec![], validator_count: 0, + minimum_validator_count: 0, bonding_duration: 0, transaction_base_fee: 10, transaction_byte_fee: 0, diff --git a/substrate/runtime/staking/src/genesis_config.rs b/substrate/runtime/staking/src/genesis_config.rs index eca4867739cf1..6b85563301652 100644 --- a/substrate/runtime/staking/src/genesis_config.rs +++ b/substrate/runtime/staking/src/genesis_config.rs @@ -39,7 +39,8 @@ pub struct GenesisConfig { pub current_era: T::BlockNumber, pub balances: Vec<(T::AccountId, T::Balance)>, pub intentions: Vec, - pub validator_count: u64, + pub validator_count: u32, + pub minimum_validator_count: u32, pub bonding_duration: T::BlockNumber, pub transaction_base_fee: T::Balance, pub transaction_byte_fee: T::Balance, @@ -60,6 +61,7 @@ impl GenesisConfig where T::AccountId: From { balances: vec![(T::AccountId::from(1), T::Balance::sa(111))], intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)], validator_count: 3, + minimum_validator_count: 1, bonding_duration: T::BlockNumber::sa(0), transaction_base_fee: T::Balance::sa(0), transaction_byte_fee: T::Balance::sa(0), @@ -88,6 +90,7 @@ impl GenesisConfig where T::AccountId: From { ], intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)], validator_count: 3, + minimum_validator_count: 1, bonding_duration: T::BlockNumber::sa(0), transaction_base_fee: T::Balance::sa(1), transaction_byte_fee: T::Balance::sa(0), @@ -110,6 +113,7 @@ impl Default for GenesisConfig { balances: vec![], intentions: vec![], validator_count: 0, + minimum_validator_count: 0, bonding_duration: T::BlockNumber::sa(1000), transaction_base_fee: T::Balance::sa(0), transaction_byte_fee: T::Balance::sa(0), @@ -133,6 +137,7 @@ impl primitives::BuildStorage for GenesisConfig { Self::hash(>::key()).to_vec() => self.intentions.encode(), Self::hash(>::key()).to_vec() => self.sessions_per_era.encode(), Self::hash(>::key()).to_vec() => self.validator_count.encode(), + Self::hash(>::key()).to_vec() => self.minimum_validator_count.encode(), Self::hash(>::key()).to_vec() => self.bonding_duration.encode(), Self::hash(>::key()).to_vec() => self.transaction_base_fee.encode(), Self::hash(>::key()).to_vec() => self.transaction_byte_fee.encode(), diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index 32a7924e57ae6..554d9387ea8fb 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -64,7 +64,7 @@ mod genesis_config; #[cfg(feature = "std")] pub use genesis_config::GenesisConfig; -const MINIMUM_REQUIRED_VALIDATORS: usize = 4; +const DEFAULT_MINIMUM_VALIDATOR_COUNT: usize = 4; /// Number of account IDs stored per enum set. const ENUM_SET_SIZE: usize = 64; @@ -175,8 +175,10 @@ decl_storage! { // The length of the bonding duration in eras. pub BondingDuration get(bonding_duration): b"sta:loc" => required T::BlockNumber; - // The length of a staking era in sessions. + // The ideal number of staking participants. pub ValidatorCount get(validator_count): b"sta:vac" => required u32; + // Minimum number of staking participants before emergency conditions are imposed. + pub MinimumValidatorCount: b"sta:minimum_validator_count" => u32; // The length of a staking era in sessions. pub SessionsPerEra get(sessions_per_era): b"sta:spe" => required T::BlockNumber; // The total amount of stake on the system. @@ -280,6 +282,10 @@ impl Module { // PUBLIC IMMUTABLES + pub fn minimum_validator_count() -> usize { + >::get().map(|v| v as usize).unwrap_or(DEFAULT_MINIMUM_VALIDATOR_COUNT) + } + /// The length of a staking era in blocks. pub fn era_length() -> T::BlockNumber { Self::sessions_per_era() * >::length() @@ -403,7 +409,7 @@ impl Module { /// Effects will be felt at the beginning of the next era. fn unstake(aux: &T::PublicAux, intentions_index: u32) -> Result { // unstake fails in degenerate case of having too few existing staked parties - if Self::intentions().len() <= MINIMUM_REQUIRED_VALIDATORS { + if Self::intentions().len() <= Self::minimum_validator_count() { return Err("cannot unstake when there are too few staked participants") } Self::apply_unstake(aux.ref_into(), intentions_index as usize) @@ -742,7 +748,7 @@ impl Module { fn slash_validator(v: &T::AccountId, slash: T::Balance) { // skip the slash in degenerate case of having only 4 staking participants despite having a larger // desired number of validators (validator_count). - if Self::intentions().len() <= MINIMUM_REQUIRED_VALIDATORS { + if Self::intentions().len() <= Self::minimum_validator_count() { return } @@ -840,7 +846,7 @@ impl Module { .collect::>(); // Avoid making new era if it would leave us with fewer than the minimum needed validators - if intentions.len() < MINIMUM_REQUIRED_VALIDATORS { + if intentions.len() < Self::minimum_validator_count() { return } diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs index e0dc6ce4dcb5f..63986954ad213 100644 --- a/substrate/runtime/staking/src/mock.rs +++ b/substrate/runtime/staking/src/mock.rs @@ -89,6 +89,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 }, intentions: vec![], validator_count: 2, + minimum_validator_count: 0, bonding_duration: 3, transaction_base_fee: 0, transaction_byte_fee: 0, From b85cb386a8f7e8b629c01d81c28e2d5aeb7178c8 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 22 Aug 2018 19:45:58 +0200 Subject: [PATCH 09/16] test fixes --- substrate/runtime/staking/src/genesis_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/runtime/staking/src/genesis_config.rs b/substrate/runtime/staking/src/genesis_config.rs index 6b85563301652..8da72bf1d92ab 100644 --- a/substrate/runtime/staking/src/genesis_config.rs +++ b/substrate/runtime/staking/src/genesis_config.rs @@ -29,7 +29,7 @@ use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra, BondingDuration, CreationFee, TransferFee, ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalStake, SessionsPerEra, ValidatorCount, FreeBalance, SessionReward, EarlyEraSlash, - OfflineSlashGrace}; + OfflineSlashGrace, MinimumValidatorCount}; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] From 71920016a181ab9e1400749e25cf9a4a57d23402 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 22 Aug 2018 21:18:47 +0200 Subject: [PATCH 10/16] Fix tests. --- substrate/runtime/staking/src/mock.rs | 2 +- substrate/runtime/staking/src/tests.rs | 17 ++--------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs index 63986954ad213..2efeab4d30743 100644 --- a/substrate/runtime/staking/src/mock.rs +++ b/substrate/runtime/staking/src/mock.rs @@ -87,7 +87,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 } else { vec![(10, balance_factor), (20, balance_factor)] }, - intentions: vec![], + intentions: vec![10, 20], validator_count: 2, minimum_validator_count: 0, bonding_duration: 3, diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs index a80930089c14d..dc04c2a42dfe0 100644 --- a/substrate/runtime/staking/src/tests.rs +++ b/substrate/runtime/staking/src/tests.rs @@ -97,8 +97,6 @@ fn note_missed_proposal_force_unstake_session_change_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { Staking::set_free_balance(&10, 70); Staking::set_free_balance(&20, 70); - assert_ok!(Staking::stake(&10)); - assert_ok!(Staking::stake(&20)); assert_ok!(Staking::stake(&1)); assert_eq!(Staking::slash_count(&10), 0); @@ -123,8 +121,6 @@ fn note_missed_proposal_auto_unstake_session_change_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { Staking::set_free_balance(&10, 7000); Staking::set_free_balance(&20, 7000); - assert_ok!(Staking::stake(&10)); - assert_ok!(Staking::stake(&20)); assert_ok!(Staking::register_slash_preference(&10, 0, SlashPreference { unstake_threshold: 1 })); assert_eq!(Staking::intentions(), vec![10, 20]); @@ -205,14 +201,12 @@ fn slashing_should_work() { assert_eq!(Staking::voting_balance(&10), 1); System::set_block_number(3); - Timestamp::set_timestamp(15); // on time. Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 1); assert_eq!(Staking::voting_balance(&10), 11); System::set_block_number(6); - Timestamp::set_timestamp(30); // on time. Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 2); @@ -220,10 +214,7 @@ fn slashing_should_work() { System::set_block_number(7); ::system::ExtrinsicIndex::::put(1); - Session::note_offline(&0, vec![0]).unwrap(); // val 10 reported bad. - Session::check_rotate_session(); - assert_eq!(Staking::current_era(), 1); - assert_eq!(Session::current_index(), 3); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); assert_eq!(Staking::voting_balance(&10), 1); }); } @@ -444,11 +435,7 @@ fn nominating_slashes_should_work() { System::set_block_number(5); ::system::ExtrinsicIndex::::put(1); - Session::note_offline(&0, vec![0, 1]).unwrap(); // both get reported offline. - assert_eq!(Session::blocks_remaining(), 1); - Session::check_rotate_session(); - - assert_eq!(Staking::current_era(), 2); + assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); assert_eq!(Staking::voting_balance(&1), 0); assert_eq!(Staking::voting_balance(&2), 20); assert_eq!(Staking::voting_balance(&3), 10); From 5a19b7b5c85f35a582e5a16e73e93470606d38fb Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 23 Aug 2018 07:40:18 +0100 Subject: [PATCH 11/16] Fix tests --- demo/executor/src/lib.rs | 6 +++--- substrate/runtime/executive/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 5bfe7ca176132..03e97ef5b1411 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -252,7 +252,7 @@ mod tests { // Blake // hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(), // Keccak - hex!("6be281d6389f65b144f08acac551207f21bfbc136077b3d385b21fac31cb8818").into(), + hex!("856f39cc430b2ecc2b94f55f0df44b28a25ab3ed341a60bdf0b8f382616f675f").into(), vec![BareExtrinsic { signed: alice(), index: 0, @@ -268,7 +268,7 @@ mod tests { // Blake // hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(), // Keccak - hex!("0e39bc5df47bf469d563b1da5d8cd492e7a79c4615229e8b62da1829e92abd5f").into(), + hex!("32cb12103306811f4febf3a93c893ebd896f0df5bcf285912d406b43d9f041aa").into(), vec![ BareExtrinsic { signed: bob(), @@ -291,7 +291,7 @@ mod tests { // Blake // hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(), // Keccak - hex!("7b6e9f810656d5d34d0034918e029ca370a189d7d69a16215875e82906440b96").into(), + hex!("f7bdc5a3409738285c04585ec436c5c9c3887448f7cf1b5086664517681eb7c1").into(), vec![BareExtrinsic { signed: alice(), index: 0, diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 1e58dcf5713a1..57b9a993c4df0 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -319,7 +319,7 @@ mod tests { // Blake // state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(), // Keccak - state_root: hex!("06efddda99014ce420dc903e6c8b7f87a1c96e699fbb43d26dc5f3203ae94ee0").into(), + state_root: hex!("ed456461b82664990b6ebd1caf1360056f6e8a062e73fada331e1c92cd81cad4").into(), extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), digest: Digest { logs: vec![], }, }, @@ -353,7 +353,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("06efddda99014ce420dc903e6c8b7f87a1c96e699fbb43d26dc5f3203ae94ee0").into(), + state_root: hex!("ed456461b82664990b6ebd1caf1360056f6e8a062e73fada331e1c92cd81cad4").into(), extrinsics_root: [0u8; 32].into(), digest: Digest { logs: vec![], }, }, From bc9f55b2452c60076bbe1c2d7119a984c81dbe80 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 25 Aug 2018 12:41:40 +0200 Subject: [PATCH 12/16] Fix tests, update readme. --- README.adoc | 30 ++++++++++++++++++++++++-- substrate/runtime/staking/src/tests.rs | 2 -- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.adoc b/README.adoc index 7c8be50e964eb..3e11d84eb6fa6 100644 --- a/README.adoc +++ b/README.adoc @@ -1,4 +1,30 @@ # Substrate -Framework for blockchain innovators. -More to come here. +Next-generation framework for blockchain innovation. + +## Description + +At its heart, Substrate is a combination of three technologies: WebAssembly, Libp2p and AfG Consensus. It is both a library for building new blockchains with and a "skeleton key" of a blockchain client, able to synchronise to any Substrate-based chain. + +Substrate chains have two distinct features that make them "next-generation": a dynamic, self-defining state-transition function and a progressive consensus algorithm with fast block production and adaptive, definite finality. The STF, encoded in WebAssembly, is known as the "runtime". This defines the `execute_block` function, and can specify everything from the staking algorithm, transaction semantics, logging mechanisms and governance procedures. Because the runtime is entirely dynamic all of these can be switched out or upgraded at any time. A Substrate chain is very much a "living organism". + +## Roadmap + +### So far + +- 0.1 "PoC-1": PBFT consensus, Wasm runtime engine, basic runtime modules. +- 0.2 "PoC-2": Libp2p + +### In progress + +- AfG consensus +- Improved PoS +- Smart contract runtime module + +### The future + +- Splitting out runtime modules into separate repo +- Introduce substrate executable (the skeleton-key runtime) +- Introduce basic but extensible transaction queue and block-builder and place them in the executable. +- DAO runtime module +- Audit diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs index dc04c2a42dfe0..122b9202dde6b 100644 --- a/substrate/runtime/staking/src/tests.rs +++ b/substrate/runtime/staking/src/tests.rs @@ -213,7 +213,6 @@ fn slashing_should_work() { assert_eq!(Staking::voting_balance(&10), 21); System::set_block_number(7); - ::system::ExtrinsicIndex::::put(1); assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); assert_eq!(Staking::voting_balance(&10), 1); }); @@ -434,7 +433,6 @@ fn nominating_slashes_should_work() { assert_eq!(Staking::voting_balance(&4), 40); System::set_block_number(5); - ::system::ExtrinsicIndex::::put(1); assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); assert_eq!(Staking::voting_balance(&1), 0); assert_eq!(Staking::voting_balance(&2), 20); From 97ed7b9a382c131b4874874e14d0a31807fb4e77 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 25 Aug 2018 12:50:03 +0200 Subject: [PATCH 13/16] Test with release. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ace14de91d142..4cba2fe46ad29 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,7 +57,7 @@ test:rust:stable: &test - export PATH="${CI_PROJECT_DIR}/cargo/bin/:$PATH" - ./scripts/build.sh - ./scripts/build-demos.sh - - time cargo test --all + - time cargo test --all --release tags: - rust-stable From 03631c6e45fa0dbc29cbeae940a2160428dd9219 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 25 Aug 2018 13:16:24 +0200 Subject: [PATCH 14/16] Use safe math when dealing with total stake --- substrate/runtime/session/Cargo.toml | 1 + substrate/runtime/staking/Cargo.toml | 1 + substrate/runtime/staking/src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/substrate/runtime/session/Cargo.toml b/substrate/runtime/session/Cargo.toml index 13244273c623f..481653125b941 100644 --- a/substrate/runtime/session/Cargo.toml +++ b/substrate/runtime/session/Cargo.toml @@ -34,4 +34,5 @@ std = [ "substrate-runtime-primitives/std", "substrate-runtime-consensus/std", "substrate-runtime-system/std", + "substrate-runtime-timestamp/std" ] diff --git a/substrate/runtime/staking/Cargo.toml b/substrate/runtime/staking/Cargo.toml index 4ca513a1f0c9d..d4fb598d8968f 100644 --- a/substrate/runtime/staking/Cargo.toml +++ b/substrate/runtime/staking/Cargo.toml @@ -40,4 +40,5 @@ std = [ "substrate-runtime-primitives/std", "substrate-runtime-session/std", "substrate-runtime-system/std", + "substrate-runtime-timestamp/std" ] diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index 0013d99ee042c..fc20150e76186 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -978,14 +978,14 @@ impl Module { /// Increase TotalStake by Value. pub fn increase_total_stake_by(value: T::Balance) { - if >::exists() { - >::put(>::total_stake() + value); + if let Some(v) = >::total_stake().checked_add(&value) { + >::put(v); } } /// Decrease TotalStake by Value. pub fn decrease_total_stake_by(value: T::Balance) { - if >::exists() { - >::put(>::total_stake() - value); + if let Some(v) = >::total_stake().checked_sub(&value) { + >::put(v); } } } From 94bebf627110e261e1d8a6e590986a0da5f27e40 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 25 Aug 2018 13:36:45 +0200 Subject: [PATCH 15/16] Fix test again. --- demo/executor/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 03e97ef5b1411..a75885bd911f2 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -102,6 +102,7 @@ mod tests { fn panic_execution_with_foreign_code_gives_error() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], @@ -121,6 +122,7 @@ mod tests { fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], @@ -140,6 +142,7 @@ mod tests { fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], @@ -163,6 +166,7 @@ mod tests { fn successful_execution_with_foreign_code_gives_ok() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], @@ -366,6 +370,7 @@ mod tests { fn panic_execution_gives_error() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![70u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], @@ -386,6 +391,7 @@ mod tests { fn successful_execution_gives_ok() { let mut t: TestExternalities = map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(>::key()).to_vec() => vec![0u8; 8], From 5d8bc21e668272bfc38c321fdede1ba235863dd2 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 27 Aug 2018 14:57:18 +0100 Subject: [PATCH 16/16] Fix grumbles. --- Cargo.lock | 1 + substrate/runtime/session/src/lib.rs | 4 ++-- substrate/runtime/staking/Cargo.toml | 2 ++ substrate/runtime/staking/src/lib.rs | 26 ++++++++------------------ 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c49662d9772d4..859ee0a5f2553 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2833,6 +2833,7 @@ dependencies = [ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", "substrate-runtime-consensus 0.1.0", diff --git a/substrate/runtime/session/src/lib.rs b/substrate/runtime/session/src/lib.rs index fdf780c4df927..5583843fd960d 100644 --- a/substrate/runtime/session/src/lib.rs +++ b/substrate/runtime/session/src/lib.rs @@ -64,7 +64,7 @@ impl OnSessionChange for () { } pub trait Trait: timestamp::Trait { - // the position of the required timestamp-set extrinsic. + // the position of the required note_missed_proposal extrinsic. const NOTE_MISSED_PROPOSAL_POSITION: u32; type ConvertAccountIdToSessionKey: Convert; @@ -152,7 +152,7 @@ impl Module { assert!(aux.is_empty()); assert!( >::extrinsic_index() == T::NOTE_MISSED_PROPOSAL_POSITION, - "note_offline extrinsic must be at position {} in the block", + "note_missed_proposal extrinsic must be at position {} in the block", T::NOTE_MISSED_PROPOSAL_POSITION ); diff --git a/substrate/runtime/staking/Cargo.toml b/substrate/runtime/staking/Cargo.toml index d4fb598d8968f..7cffe09d01217 100644 --- a/substrate/runtime/staking/Cargo.toml +++ b/substrate/runtime/staking/Cargo.toml @@ -10,6 +10,7 @@ serde_derive = { version = "1.0", optional = true } safe-mix = { version = "1.0", default_features = false} substrate-keyring = { path = "../../keyring", optional = true } substrate-codec = { path = "../../codec", default_features = false } +substrate-codec-derive = { path = "../../codec/derive", default_features = false } substrate-primitives = { path = "../../primitives", default_features = false } substrate-runtime-std = { path = "../../runtime-std", default_features = false } substrate-runtime-io = { path = "../../runtime-io", default_features = false } @@ -32,6 +33,7 @@ std = [ "safe-mix/std", "substrate-keyring", "substrate-codec/std", + "substrate-codec-derive/std", "substrate-primitives/std", "substrate-runtime-std/std", "substrate-runtime-io/std", diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index fc20150e76186..dd1e6fcc2d5ca 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -34,6 +34,9 @@ extern crate substrate_runtime_support as runtime_support; #[cfg_attr(feature = "std", macro_use)] extern crate substrate_runtime_std as rstd; +#[macro_use] +extern crate substrate_codec_derive; + extern crate substrate_codec as codec; extern crate substrate_primitives; extern crate substrate_runtime_io as runtime_io; @@ -102,26 +105,12 @@ impl OnAccountKill for () { /// Preference of what happens on a slash event. #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -#[derive(Eq, PartialEq, Clone, Copy)] +#[derive(Encode, Decode, Eq, PartialEq, Clone, Copy)] pub struct SlashPreference { /// Validator should ensure this many more slashes than is necessary before being unstaked. pub unstake_threshold: u32, } -impl Decode for SlashPreference { - fn decode(input: &mut I) -> Option { - Some(SlashPreference { - unstake_threshold: Decode::decode(input)? - }) - } -} - -impl Encode for SlashPreference { - fn encode_to(&self, dest: &mut T) { - self.unstake_threshold.encode_to(dest) - } -} - impl Default for SlashPreference { fn default() -> Self { SlashPreference { @@ -549,8 +538,8 @@ impl Module { Ok(()) } - /// Force there to be a new era. This also forces a new session immediately after by - /// setting `normal_rotation` to be false. Validators will get slashed. + /// Force there to be a new era. This also forces a new session immediately after. + /// `apply_rewards` should be true for validators to get the session reward. fn force_new_era(apply_rewards: bool) -> Result { >::put(()); >::force_new_session(apply_rewards) @@ -853,7 +842,8 @@ impl Module { .map(|v| (Self::slashable_balance(&v), v)) .collect::>(); - // Avoid making new era if it would leave us with fewer than the minimum needed validators + // Avoid reevaluate validator set if it would leave us with fewer than the minimum + // needed validators if intentions.len() < Self::minimum_validator_count() { return }