mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-22 21:55:45 +00:00
Track received attestations from recent blocks (#337)
* record attestations in storage ringbuffer * remove some reliance on Aura * fix up test configuration * extract attestations stuff out to its own module * add dummy inherent * use double_map * fix a couple more compilation errors
This commit is contained in:
committed by
GitHub
parent
8c4d882407
commit
4d19de4bd9
@@ -0,0 +1,265 @@
|
|||||||
|
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Data structures and synchronous logic for ICMP message gossip.
|
||||||
|
|
||||||
|
use sr_primitives::traits::{BlakeTwo256, Hash as HashT};
|
||||||
|
use polkadot_primitives::Hash;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use substrate_client::error::Error as ClientError;
|
||||||
|
use super::{MAX_CHAIN_HEADS, GossipValidationResult, LeavesVec, ChainContext};
|
||||||
|
|
||||||
|
/// Construct a topic for a message queue root deterministically.
|
||||||
|
pub fn queue_topic(queue_root: Hash) -> Hash {
|
||||||
|
let mut v = queue_root.as_ref().to_vec();
|
||||||
|
v.extend(b"message_queue");
|
||||||
|
|
||||||
|
BlakeTwo256::hash(&v[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view of which queue roots are current for a given set of leaves.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct View {
|
||||||
|
leaves: LeavesVec,
|
||||||
|
leaf_topics: HashMap<Hash, HashSet<Hash>>, // leaf_hash -> { topics }
|
||||||
|
expected_queues: HashMap<Hash, Hash>, // topic -> queue-root
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
/// Update the set of current leaves.
|
||||||
|
pub fn update_leaves<T: ChainContext + ?Sized, I>(&mut self, context: &T, new_leaves: I)
|
||||||
|
-> Result<(), ClientError>
|
||||||
|
where I: Iterator<Item=Hash>
|
||||||
|
{
|
||||||
|
let new_leaves = new_leaves.take(MAX_CHAIN_HEADS);
|
||||||
|
let old_leaves = {
|
||||||
|
let mut new = LeavesVec::new();
|
||||||
|
for leaf in new_leaves {
|
||||||
|
new.push(leaf.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mem::replace(&mut self.leaves, new)
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_queues = &mut self.expected_queues;
|
||||||
|
let leaves = &self.leaves;
|
||||||
|
self.leaf_topics.retain(|l, topics| {
|
||||||
|
if leaves.contains(l) { return true }
|
||||||
|
|
||||||
|
// prune out all data about old leaves we don't follow anymore.
|
||||||
|
for topic in topics.iter() {
|
||||||
|
expected_queues.remove(topic);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut res = Ok(());
|
||||||
|
|
||||||
|
// add in new data about fresh leaves.
|
||||||
|
for new_leaf in &self.leaves {
|
||||||
|
if old_leaves.contains(new_leaf) { continue }
|
||||||
|
|
||||||
|
let mut this_leaf_topics = HashSet::new();
|
||||||
|
|
||||||
|
let r = context.leaf_unrouted_roots(new_leaf, &mut |&queue_root| {
|
||||||
|
let topic = queue_topic(queue_root);
|
||||||
|
this_leaf_topics.insert(topic);
|
||||||
|
expected_queues.insert(topic, queue_root);
|
||||||
|
});
|
||||||
|
|
||||||
|
if r.is_err() {
|
||||||
|
res = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.leaf_topics.insert(*new_leaf, this_leaf_topics);
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate an incoming message queue against this view.
|
||||||
|
pub fn validate_queue(&self, messages: &super::GossipParachainMessages)
|
||||||
|
-> (GossipValidationResult<Hash>, i32)
|
||||||
|
{
|
||||||
|
let ostensible_topic = queue_topic(messages.queue_root);
|
||||||
|
if !self.is_topic_live(&ostensible_topic) {
|
||||||
|
(GossipValidationResult::Discard, super::cost::UNNEEDED_ICMP_MESSAGES)
|
||||||
|
} else if !messages.queue_root_is_correct() {
|
||||||
|
(
|
||||||
|
GossipValidationResult::Discard,
|
||||||
|
super::cost::icmp_messages_root_mismatch(messages.messages.len()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
GossipValidationResult::ProcessAndKeep(ostensible_topic),
|
||||||
|
super::benefit::NEW_ICMP_MESSAGES,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a message with given topic is live.
|
||||||
|
pub fn is_topic_live(&self, topic: &Hash) -> bool {
|
||||||
|
self.expected_queues.get(topic).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a message is allowed under the intersection of the given leaf-set
|
||||||
|
/// and our own.
|
||||||
|
pub fn allowed_intersecting(&self, other_leaves: &LeavesVec, topic: &Hash) -> bool {
|
||||||
|
for i in other_leaves {
|
||||||
|
for j in &self.leaves {
|
||||||
|
if i == j {
|
||||||
|
let leaf_topics = self.leaf_topics.get(i)
|
||||||
|
.expect("leaf_topics are mutated only in update_leaves; \
|
||||||
|
we have an entry for each item in self.leaves; \
|
||||||
|
i is in self.leaves; qed");
|
||||||
|
|
||||||
|
if leaf_topics.contains(topic) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::tests::TestChainContext;
|
||||||
|
use crate::gossip::{Known, GossipParachainMessages};
|
||||||
|
use polkadot_primitives::parachain::Message as ParachainMessage;
|
||||||
|
|
||||||
|
fn hash(x: u8) -> Hash {
|
||||||
|
[x; 32].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message_queue(from: u8, to: u8) -> Option<[[u8; 2]; 1]> {
|
||||||
|
if from == to {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some([[from, to]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message_queue_root(from: u8, to: u8) -> Option<Hash> {
|
||||||
|
message_queue(from, to).map(
|
||||||
|
|q| polkadot_validation::message_queue_root(q.iter())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_roots(view: &View, i: u8, max: u8) -> bool {
|
||||||
|
for j in 0..max {
|
||||||
|
if let Some(messages) = message_queue(i, j) {
|
||||||
|
let queue_root = message_queue_root(i, j).unwrap();
|
||||||
|
let messages = GossipParachainMessages {
|
||||||
|
queue_root,
|
||||||
|
messages: messages.iter().map(|m| ParachainMessage(m.to_vec())).collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match view.validate_queue(&messages).0 {
|
||||||
|
GossipValidationResult::ProcessAndKeep(topic) => if topic != queue_topic(queue_root) {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_leaves_none_in_common() {
|
||||||
|
let mut ctx = TestChainContext::default();
|
||||||
|
let max = 5;
|
||||||
|
|
||||||
|
for i in 0..max {
|
||||||
|
ctx.known_map.insert(hash(i as u8), Known::Leaf);
|
||||||
|
|
||||||
|
let messages_out: Vec<_> = (0..max).filter_map(|j| message_queue_root(i, j)).collect();
|
||||||
|
|
||||||
|
if !messages_out.is_empty() {
|
||||||
|
ctx.ingress_roots.insert(hash(i as u8), messages_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut view = View::default();
|
||||||
|
view.update_leaves(
|
||||||
|
&ctx,
|
||||||
|
[hash(0), hash(1)].iter().cloned(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(check_roots(&view, 0, max));
|
||||||
|
assert!(check_roots(&view, 1, max));
|
||||||
|
|
||||||
|
assert!(!check_roots(&view, 2, max));
|
||||||
|
assert!(!check_roots(&view, 3, max));
|
||||||
|
assert!(!check_roots(&view, 4, max));
|
||||||
|
assert!(!check_roots(&view, 5, max));
|
||||||
|
|
||||||
|
view.update_leaves(
|
||||||
|
&ctx,
|
||||||
|
[hash(2), hash(3), hash(4)].iter().cloned(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(!check_roots(&view, 0, max));
|
||||||
|
assert!(!check_roots(&view, 1, max));
|
||||||
|
|
||||||
|
assert!(check_roots(&view, 2, max));
|
||||||
|
assert!(check_roots(&view, 3, max));
|
||||||
|
assert!(check_roots(&view, 4, max));
|
||||||
|
|
||||||
|
assert!(!check_roots(&view, 5, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_leaves_overlapping() {
|
||||||
|
let mut ctx = TestChainContext::default();
|
||||||
|
let max = 5;
|
||||||
|
|
||||||
|
for i in 0..max {
|
||||||
|
ctx.known_map.insert(hash(i as u8), Known::Leaf);
|
||||||
|
|
||||||
|
let messages_out: Vec<_> = (0..max).filter_map(|j| message_queue_root(i, j)).collect();
|
||||||
|
|
||||||
|
if !messages_out.is_empty() {
|
||||||
|
ctx.ingress_roots.insert(hash(i as u8), messages_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut view = View::default();
|
||||||
|
view.update_leaves(
|
||||||
|
&ctx,
|
||||||
|
[hash(0), hash(1), hash(2)].iter().cloned(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
view.update_leaves(
|
||||||
|
&ctx,
|
||||||
|
[hash(2), hash(3), hash(4)].iter().cloned(),
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(!check_roots(&view, 0, max));
|
||||||
|
assert!(!check_roots(&view, 1, max));
|
||||||
|
|
||||||
|
assert!(check_roots(&view, 2, max));
|
||||||
|
assert!(check_roots(&view, 3, max));
|
||||||
|
assert!(check_roots(&view, 4, max));
|
||||||
|
|
||||||
|
assert!(!check_roots(&view, 5, max));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,13 @@ pub type AuraPair = ed25519::Pair;
|
|||||||
/// The Ed25519 pub key of an session that belongs to an Aura authority of the chain.
|
/// The Ed25519 pub key of an session that belongs to an Aura authority of the chain.
|
||||||
pub type AuraId = ed25519::Public;
|
pub type AuraId = ed25519::Public;
|
||||||
|
|
||||||
|
/// The Parachain crypto scheme defined via the keypair type.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub type ParachainPair = ed25519::Pair;
|
||||||
|
|
||||||
|
/// The Ed25519 public key used to authenticate signatures on parachain data.
|
||||||
|
pub type ParachainPublic = ed25519::Public;
|
||||||
|
|
||||||
/// Header type.
|
/// Header type.
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
/// Block type.
|
/// Block type.
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! A module for tracking all attestations that fell on a given candidate receipt.
|
||||||
|
//!
|
||||||
|
//! In the future, it is planned that this module will handle dispute resolution
|
||||||
|
//! as well.
|
||||||
|
|
||||||
|
use rstd::prelude::*;
|
||||||
|
use parity_codec::{Encode, Decode};
|
||||||
|
use srml_support::{decl_storage, decl_module, ensure};
|
||||||
|
|
||||||
|
use primitives::{Hash, parachain::{AttestedCandidate, CandidateReceipt, Id as ParaId}};
|
||||||
|
use {system, session::{self, SessionIndex}};
|
||||||
|
use srml_support::{
|
||||||
|
StorageValue, StorageMap, StorageDoubleMap, dispatch::Result, traits::Get,
|
||||||
|
};
|
||||||
|
|
||||||
|
use inherents::{ProvideInherent, InherentData, RuntimeString, MakeFatalError, InherentIdentifier};
|
||||||
|
use system::ensure_none;
|
||||||
|
|
||||||
|
/// Parachain blocks included in a recent relay-chain block.
|
||||||
|
#[derive(Encode, Decode)]
|
||||||
|
pub struct IncludedBlocks<T: Trait> {
|
||||||
|
/// The actual relay chain block number where blocks were included.
|
||||||
|
pub actual_number: T::BlockNumber,
|
||||||
|
/// The session index at this block.
|
||||||
|
pub session: SessionIndex,
|
||||||
|
/// The randomness seed at this block.
|
||||||
|
pub random_seed: [u8; 32],
|
||||||
|
/// All parachain IDs active at this block.
|
||||||
|
pub active_parachains: Vec<ParaId>,
|
||||||
|
/// Hashes of the parachain candidates included at this block.
|
||||||
|
pub para_blocks: Vec<Hash>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attestations kept over time on a parachain block.
|
||||||
|
#[derive(Encode, Decode)]
|
||||||
|
pub struct BlockAttestations<T: Trait> {
|
||||||
|
receipt: CandidateReceipt,
|
||||||
|
valid: Vec<T::AccountId>, // stash account ID of voter.
|
||||||
|
invalid: Vec<T::AccountId>, // stash account ID of voter.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additional attestations on a parachain block, after it was included.
|
||||||
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Debug))]
|
||||||
|
pub struct MoreAttestations;
|
||||||
|
|
||||||
|
pub trait Trait: session::Trait {
|
||||||
|
/// How many blocks ago we're willing to accept attestations for.
|
||||||
|
type AttestationPeriod: Get<Self::BlockNumber>;
|
||||||
|
|
||||||
|
/// Get a list of the validators' underlying identities.
|
||||||
|
type ValidatorIdentities: Get<Vec<Self::AccountId>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
decl_storage! {
|
||||||
|
trait Store for Module<T: Trait> as Attestations {
|
||||||
|
/// A mapping from modular block number (n % AttestationPeriod)
|
||||||
|
/// to session index and the list of candidate hashes.
|
||||||
|
pub RecentParaBlocks: map T::BlockNumber => Option<IncludedBlocks<T>>;
|
||||||
|
|
||||||
|
/// Attestations on a recent parachain block.
|
||||||
|
pub ParaBlockAttestations: double_map T::BlockNumber, blake2_128(Hash) => Option<BlockAttestations<T>>;
|
||||||
|
|
||||||
|
// Did we already have more attestations included in this block?
|
||||||
|
DidUpdate: bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decl_module! {
|
||||||
|
/// Parachain-attestations module.
|
||||||
|
pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
|
||||||
|
/// Provide candidate receipts for parachains, in ascending order by id.
|
||||||
|
fn more_attestations(origin, _more: MoreAttestations) -> Result {
|
||||||
|
ensure_none(origin)?;
|
||||||
|
ensure!(!<DidUpdate>::exists(), "More attestations can be added only once in a block.");
|
||||||
|
<DidUpdate>::put(true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_finalize(_n: T::BlockNumber) {
|
||||||
|
<DidUpdate>::kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Trait> Module<T> {
|
||||||
|
/// Update recent candidates to contain the already-checked parachain candidates.
|
||||||
|
pub(crate) fn note_included(heads: &[AttestedCandidate], para_blocks: IncludedBlocks<T>) {
|
||||||
|
let attestation_period = T::AttestationPeriod::get();
|
||||||
|
let mod_num = para_blocks.actual_number % attestation_period;
|
||||||
|
|
||||||
|
// clear old entry that was in this place.
|
||||||
|
if let Some(old_entry) = <RecentParaBlocks<T>>::take(&mod_num) {
|
||||||
|
<ParaBlockAttestations<T>>::remove_prefix(&old_entry.actual_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
let validators = T::ValidatorIdentities::get();
|
||||||
|
|
||||||
|
// make new entry.
|
||||||
|
for (head, hash) in heads.iter().zip(¶_blocks.para_blocks) {
|
||||||
|
let mut valid = Vec::new();
|
||||||
|
let invalid = Vec::new();
|
||||||
|
|
||||||
|
for (auth_index, _) in head.validator_indices
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, bit)| *bit)
|
||||||
|
{
|
||||||
|
let stash_id = validators.get(auth_index)
|
||||||
|
.expect("auth_index checked to be within bounds in `check_candidates`; qed")
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
valid.push(stash_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let summary = BlockAttestations {
|
||||||
|
receipt: head.candidate().clone(),
|
||||||
|
valid,
|
||||||
|
invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
<ParaBlockAttestations<T>>::insert(¶_blocks.actual_number, hash, &summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
<RecentParaBlocks<T>>::insert(&mod_num, ¶_blocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An identifier for inherent data that provides after-the-fact attestations
|
||||||
|
/// on already included parachain blocks.
|
||||||
|
pub const MORE_ATTESTATIONS_IDENTIFIER: InherentIdentifier = *b"par-atts";
|
||||||
|
|
||||||
|
pub type InherentType = MoreAttestations;
|
||||||
|
|
||||||
|
impl<T: Trait> ProvideInherent for Module<T> {
|
||||||
|
type Call = Call<T>;
|
||||||
|
type Error = MakeFatalError<RuntimeString>;
|
||||||
|
const INHERENT_IDENTIFIER: InherentIdentifier = MORE_ATTESTATIONS_IDENTIFIER;
|
||||||
|
|
||||||
|
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
||||||
|
data.get_data::<InherentType>(&MORE_ATTESTATIONS_IDENTIFIER)
|
||||||
|
.ok()
|
||||||
|
.and_then(|x| x.map(Call::more_attestations))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,15 +14,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm.
|
//! The Polkadot runtime. This can be compiled with `#[no_std]`, ready for Wasm.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
||||||
#![recursion_limit="256"]
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
|
mod attestations;
|
||||||
|
mod claims;
|
||||||
mod curated_grandpa;
|
mod curated_grandpa;
|
||||||
mod parachains;
|
mod parachains;
|
||||||
mod claims;
|
|
||||||
mod slot_range;
|
mod slot_range;
|
||||||
mod slots;
|
mod slots;
|
||||||
|
|
||||||
@@ -57,7 +58,8 @@ pub use staking::StakerStatus;
|
|||||||
pub use sr_primitives::BuildStorage;
|
pub use sr_primitives::BuildStorage;
|
||||||
pub use timestamp::Call as TimestampCall;
|
pub use timestamp::Call as TimestampCall;
|
||||||
pub use balances::Call as BalancesCall;
|
pub use balances::Call as BalancesCall;
|
||||||
pub use parachains::{Call as ParachainsCall, INHERENT_IDENTIFIER as PARACHAIN_INHERENT_IDENTIFIER};
|
pub use attestations::{Call as AttestationsCall, MORE_ATTESTATIONS_IDENTIFIER};
|
||||||
|
pub use parachains::{Call as ParachainsCall, NEW_HEADS_IDENTIFIER};
|
||||||
pub use sr_primitives::{Permill, Perbill};
|
pub use sr_primitives::{Permill, Perbill};
|
||||||
pub use srml_support::StorageValue;
|
pub use srml_support::StorageValue;
|
||||||
|
|
||||||
@@ -244,6 +246,8 @@ parameter_types! {
|
|||||||
pub const MinimumDeposit: Balance = 100 * BUCKS;
|
pub const MinimumDeposit: Balance = 100 * BUCKS;
|
||||||
pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
|
pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
|
||||||
pub const CooloffPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
|
pub const CooloffPeriod: BlockNumber = 30 * 24 * 60 * MINUTES;
|
||||||
|
|
||||||
|
pub const AttestationPeriod: BlockNumber = 60 * MINUTES * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl democracy::Trait for Runtime {
|
impl democracy::Trait for Runtime {
|
||||||
@@ -343,6 +347,11 @@ impl finality_tracker::Trait for Runtime {
|
|||||||
type ReportLatency = ReportLatency;
|
type ReportLatency = ReportLatency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl attestations::Trait for Runtime {
|
||||||
|
type AttestationPeriod = AttestationPeriod;
|
||||||
|
type ValidatorIdentities = parachains::ValidatorIdentities<Runtime>;
|
||||||
|
}
|
||||||
|
|
||||||
impl parachains::Trait for Runtime {
|
impl parachains::Trait for Runtime {
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
@@ -392,6 +401,7 @@ construct_runtime!(
|
|||||||
CuratedGrandpa: curated_grandpa::{Module, Call, Config<T>, Storage},
|
CuratedGrandpa: curated_grandpa::{Module, Call, Config<T>, Storage},
|
||||||
Treasury: treasury::{Module, Call, Storage, Event<T>},
|
Treasury: treasury::{Module, Call, Storage, Event<T>},
|
||||||
Parachains: parachains::{Module, Call, Storage, Config<T>, Inherent, Origin},
|
Parachains: parachains::{Module, Call, Storage, Config<T>, Inherent, Origin},
|
||||||
|
Attestations: attestations::{Module, Call, Storage},
|
||||||
Slots: slots::{Module, Call, Storage, Event<T>},
|
Slots: slots::{Module, Call, Storage, Event<T>},
|
||||||
Sudo: sudo,
|
Sudo: sudo,
|
||||||
}
|
}
|
||||||
@@ -481,7 +491,7 @@ impl_runtime_apis! {
|
|||||||
Aura::authorities() // only possible as long as parachain validator crypto === aura crypto
|
Aura::authorities() // only possible as long as parachain validator crypto === aura crypto
|
||||||
}
|
}
|
||||||
fn duty_roster() -> parachain::DutyRoster {
|
fn duty_roster() -> parachain::DutyRoster {
|
||||||
Parachains::calculate_duty_roster()
|
Parachains::calculate_duty_roster().0
|
||||||
}
|
}
|
||||||
fn active_parachains() -> Vec<parachain::Id> {
|
fn active_parachains() -> Vec<parachain::Id> {
|
||||||
Parachains::active_parachains()
|
Parachains::active_parachains()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Polkadot.
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
// Polkadot is free software: you can redistribute it and/or modify
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
@@ -18,19 +18,19 @@
|
|||||||
|
|
||||||
use rstd::prelude::*;
|
use rstd::prelude::*;
|
||||||
use rstd::collections::btree_map::BTreeMap;
|
use rstd::collections::btree_map::BTreeMap;
|
||||||
use parity_codec::{Decode, HasCompact};
|
use parity_codec::{Encode, Decode, HasCompact};
|
||||||
use srml_support::{decl_storage, decl_module, fail, ensure};
|
use srml_support::{decl_storage, decl_module, fail, ensure};
|
||||||
|
|
||||||
use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One};
|
use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One};
|
||||||
use sr_primitives::weights::SimpleDispatchInfo;
|
use sr_primitives::weights::SimpleDispatchInfo;
|
||||||
use primitives::{Hash, Balance, parachain::{
|
use primitives::{Hash, Balance, ParachainPublic, parachain::{
|
||||||
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
|
self, Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
|
||||||
ParachainDispatchOrigin, UpwardMessage, BlockIngressRoots,
|
ParachainDispatchOrigin, UpwardMessage, BlockIngressRoots,
|
||||||
}};
|
}};
|
||||||
use {system, session};
|
use {system, session};
|
||||||
use srml_support::{
|
use srml_support::{
|
||||||
StorageValue, StorageMap, storage::AppendableStorageMap, Parameter, Dispatchable, dispatch::Result,
|
StorageValue, StorageMap, storage::AppendableStorageMap, Parameter, Dispatchable, dispatch::Result,
|
||||||
traits::{Currency, WithdrawReason, ExistenceRequirement}
|
traits::{Currency, Get, WithdrawReason, ExistenceRequirement}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -45,6 +45,7 @@ use sr_primitives::{StorageOverlay, ChildrenStorageOverlay};
|
|||||||
use rstd::marker::PhantomData;
|
use rstd::marker::PhantomData;
|
||||||
|
|
||||||
use system::{ensure_none, ensure_root};
|
use system::{ensure_none, ensure_root};
|
||||||
|
use crate::attestations::{self, IncludedBlocks};
|
||||||
|
|
||||||
// ranges for iteration of general block number don't work, so this
|
// ranges for iteration of general block number don't work, so this
|
||||||
// is a utility to get around that.
|
// is a utility to get around that.
|
||||||
@@ -172,7 +173,16 @@ impl<AccountId, T: Currency<AccountId>> ParachainCurrency<AccountId> for T where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Trait: session::Trait {
|
/// Interface to the persistent (stash) identities of the current validators.
|
||||||
|
pub struct ValidatorIdentities<T>(rstd::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: session::Trait> Get<Vec<T::ValidatorId>> for ValidatorIdentities<T> {
|
||||||
|
fn get() -> Vec<T::ValidatorId> {
|
||||||
|
<session::Module<T>>::validators()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Trait: attestations::Trait {
|
||||||
/// The outer origin type.
|
/// The outer origin type.
|
||||||
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>;
|
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>;
|
||||||
|
|
||||||
@@ -206,15 +216,17 @@ const WATERMARK_QUEUE_SIZE: usize = 20000;
|
|||||||
|
|
||||||
decl_storage! {
|
decl_storage! {
|
||||||
trait Store for Module<T: Trait> as Parachains {
|
trait Store for Module<T: Trait> as Parachains {
|
||||||
// Vector of all parachain IDs.
|
/// All authorities' keys at the moment.
|
||||||
|
pub Authorities get(authorities) config(authorities): Vec<ParachainPublic>;
|
||||||
|
/// Vector of all parachain IDs.
|
||||||
pub Parachains get(active_parachains): Vec<ParaId>;
|
pub Parachains get(active_parachains): Vec<ParaId>;
|
||||||
// The parachains registered at present.
|
/// The parachains registered at present.
|
||||||
pub Code get(parachain_code): map ParaId => Option<Vec<u8>>;
|
pub Code get(parachain_code): map ParaId => Option<Vec<u8>>;
|
||||||
// The heads of the parachains registered at present.
|
/// The heads of the parachains registered at present.
|
||||||
pub Heads get(parachain_head): map ParaId => Option<Vec<u8>>;
|
pub Heads get(parachain_head): map ParaId => Option<Vec<u8>>;
|
||||||
// The watermark heights of the parachains registered at present.
|
/// The watermark heights of the parachains registered at present.
|
||||||
// For every parachain, this is the block height from which all messages targeting
|
/// For every parachain, this is the block height from which all messages targeting
|
||||||
// that parachain have been processed. Can be `None` only if the parachain doesn't exist.
|
/// that parachain have been processed. Can be `None` only if the parachain doesn't exist.
|
||||||
pub Watermarks get(watermark): map ParaId => Option<T::BlockNumber>;
|
pub Watermarks get(watermark): map ParaId => Option<T::BlockNumber>;
|
||||||
|
|
||||||
/// Unrouted ingress. Maps (BlockNumber, to_chain) pairs to [(from_chain, egress_root)].
|
/// Unrouted ingress. Maps (BlockNumber, to_chain) pairs to [(from_chain, egress_root)].
|
||||||
@@ -228,10 +240,10 @@ decl_storage! {
|
|||||||
pub RelayDispatchQueue: map ParaId => Vec<UpwardMessage>;
|
pub RelayDispatchQueue: map ParaId => Vec<UpwardMessage>;
|
||||||
/// Size of the dispatch queues. Separated from actual data in order to avoid costly
|
/// Size of the dispatch queues. Separated from actual data in order to avoid costly
|
||||||
/// decoding when checking receipt validity. First item in tuple is the count of messages
|
/// decoding when checking receipt validity. First item in tuple is the count of messages
|
||||||
// second if the total length (in bytes) of the message payloads.
|
/// second if the total length (in bytes) of the message payloads.
|
||||||
pub RelayDispatchQueueSize: map ParaId => (u32, u32);
|
pub RelayDispatchQueueSize: map ParaId => (u32, u32);
|
||||||
|
|
||||||
// Did the parachain heads get updated in this block?
|
/// Did the parachain heads get updated in this block?
|
||||||
DidUpdate: bool;
|
DidUpdate: bool;
|
||||||
|
|
||||||
/// The next unused ParaId value.
|
/// The next unused ParaId value.
|
||||||
@@ -305,13 +317,15 @@ decl_module! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::check_candidates(&heads)?;
|
let para_blocks = Self::check_candidates(&heads, &active_parachains)?;
|
||||||
|
|
||||||
let current_number = <system::Module<T>>::block_number();
|
let current_number = <system::Module<T>>::block_number();
|
||||||
|
|
||||||
|
<attestations::Module<T>>::note_included(&heads, para_blocks);
|
||||||
|
|
||||||
Self::update_routing(
|
Self::update_routing(
|
||||||
current_number,
|
current_number,
|
||||||
&heads
|
&heads,
|
||||||
);
|
);
|
||||||
|
|
||||||
Self::dispatch_upward_messages(
|
Self::dispatch_upward_messages(
|
||||||
@@ -354,8 +368,6 @@ fn majority_of(list_len: usize) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn localized_payload(statement: Statement, parent_hash: ::primitives::Hash) -> Vec<u8> {
|
fn localized_payload(statement: Statement, parent_hash: ::primitives::Hash) -> Vec<u8> {
|
||||||
use parity_codec::Encode;
|
|
||||||
|
|
||||||
let mut encoded = statement.encode();
|
let mut encoded = statement.encode();
|
||||||
encoded.extend(parent_hash.as_ref());
|
encoded.extend(parent_hash.as_ref());
|
||||||
encoded
|
encoded
|
||||||
@@ -512,10 +524,11 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the current block's duty roster using system's random seed.
|
/// Calculate the current block's duty roster using system's random seed.
|
||||||
pub fn calculate_duty_roster() -> DutyRoster {
|
/// Returns the duty roster along with the random seed.
|
||||||
|
pub fn calculate_duty_roster() -> (DutyRoster, [u8; 32]) {
|
||||||
let parachains = Self::active_parachains();
|
let parachains = Self::active_parachains();
|
||||||
let parachain_count = parachains.len();
|
let parachain_count = parachains.len();
|
||||||
let validator_count = crate::Aura::authorities().len();
|
let validator_count = Self::authorities().len();
|
||||||
let validators_per_parachain = if parachain_count != 0 { (validator_count - 1) / parachain_count } else { 0 };
|
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 {
|
let mut roles_val = (0..validator_count).map(|i| match i {
|
||||||
@@ -545,6 +558,8 @@ impl<T: Trait> Module<T> {
|
|||||||
BlakeTwo256::hash(&seed.as_ref()[seed_off..])
|
BlakeTwo256::hash(&seed.as_ref()[seed_off..])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let orig_seed = seed.clone().to_fixed_bytes();
|
||||||
|
|
||||||
// shuffle
|
// shuffle
|
||||||
for i in 0..(validator_count - 1) {
|
for i in 0..(validator_count - 1) {
|
||||||
// 4 bytes of entropy used per cycle, 32 bytes entropy per hash
|
// 4 bytes of entropy used per cycle, 32 bytes entropy per hash
|
||||||
@@ -566,9 +581,7 @@ impl<T: Trait> Module<T> {
|
|||||||
roles_val.swap(remaining - 1, val_index);
|
roles_val.swap(remaining - 1, val_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
DutyRoster {
|
(DutyRoster { validator_duty: roles_val, }, orig_seed)
|
||||||
validator_duty: roles_val,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the ingress to a specific parachain.
|
/// Calculate the ingress to a specific parachain.
|
||||||
@@ -639,7 +652,9 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
// check the attestations on these candidates. The candidates should have been checked
|
// check the attestations on these candidates. The candidates should have been checked
|
||||||
// that each candidates' chain ID is valid.
|
// that each candidates' chain ID is valid.
|
||||||
fn check_candidates(attested_candidates: &[AttestedCandidate]) -> Result {
|
fn check_candidates(attested_candidates: &[AttestedCandidate], active_parachains: &[ParaId])
|
||||||
|
-> rstd::result::Result<IncludedBlocks<T>, &'static str>
|
||||||
|
{
|
||||||
use primitives::parachain::ValidityAttestation;
|
use primitives::parachain::ValidityAttestation;
|
||||||
use sr_primitives::traits::Verify;
|
use sr_primitives::traits::Verify;
|
||||||
|
|
||||||
@@ -683,8 +698,8 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let authorities = super::Aura::authorities();
|
let authorities = Self::authorities();
|
||||||
let duty_roster = Self::calculate_duty_roster();
|
let (duty_roster, random_seed) = Self::calculate_duty_roster();
|
||||||
|
|
||||||
// convert a duty roster, which is originally a Vec<Chain>, where each
|
// convert a duty roster, which is originally a Vec<Chain>, where each
|
||||||
// item corresponds to the same position in the session keys, into
|
// item corresponds to the same position in the session keys, into
|
||||||
@@ -715,6 +730,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
|
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
|
||||||
|
|
||||||
|
let mut para_block_hashes = Vec::new();
|
||||||
for candidate in attested_candidates {
|
for candidate in attested_candidates {
|
||||||
let para_id = candidate.parachain_index();
|
let para_id = candidate.parachain_index();
|
||||||
let validator_group = validator_groups.group_for(para_id)
|
let validator_group = validator_groups.group_for(para_id)
|
||||||
@@ -783,13 +799,21 @@ impl<T: Trait> Module<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure!(
|
para_block_hashes.push(candidate_hash.unwrap_or_else(|| candidate.candidate().hash()));
|
||||||
|
|
||||||
|
ensure!(
|
||||||
candidate.validity_votes.len() == expected_votes_len,
|
candidate.validity_votes.len() == expected_votes_len,
|
||||||
"Extra untagged validity votes along with candidate"
|
"Extra untagged validity votes along with candidate"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(IncludedBlocks {
|
||||||
|
actual_number: <system::Module<T>>::block_number(),
|
||||||
|
session: <session::Module<T>>::current_index(),
|
||||||
|
random_seed,
|
||||||
|
active_parachains: active_parachains.to_vec(),
|
||||||
|
para_blocks: para_block_hashes,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -809,17 +833,33 @@ impl<T: Trait> Module<T> {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"newheads";
|
impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
|
||||||
|
type Key = ParachainPublic;
|
||||||
|
|
||||||
|
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued: I)
|
||||||
|
where I: Iterator<Item=(&'a T::AccountId, Self::Key)>
|
||||||
|
{
|
||||||
|
if changed {
|
||||||
|
<Self as Store>::Authorities::put(&validators.map(|(_, key)| key).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_disabled(_i: usize) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An identifier for inherent data that provides new minimally-attested
|
||||||
|
/// parachain heads.
|
||||||
|
pub const NEW_HEADS_IDENTIFIER: InherentIdentifier = *b"newheads";
|
||||||
|
|
||||||
pub type InherentType = Vec<AttestedCandidate>;
|
pub type InherentType = Vec<AttestedCandidate>;
|
||||||
|
|
||||||
impl<T: Trait> ProvideInherent for Module<T> {
|
impl<T: Trait> ProvideInherent for Module<T> {
|
||||||
type Call = Call<T>;
|
type Call = Call<T>;
|
||||||
type Error = MakeFatalError<RuntimeString>;
|
type Error = MakeFatalError<RuntimeString>;
|
||||||
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
const INHERENT_IDENTIFIER: InherentIdentifier = NEW_HEADS_IDENTIFIER;
|
||||||
|
|
||||||
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
||||||
let data = data.get_data::<InherentType>(&INHERENT_IDENTIFIER)
|
let data = data.get_data::<InherentType>(&NEW_HEADS_IDENTIFIER)
|
||||||
.expect("Parachain heads could not be decoded.")
|
.expect("Parachain heads could not be decoded.")
|
||||||
.expect("No parachain heads found in inherent data.");
|
.expect("No parachain heads found in inherent data.");
|
||||||
|
|
||||||
@@ -949,6 +989,7 @@ mod tests {
|
|||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const SessionsPerEra: session::SessionIndex = 6;
|
pub const SessionsPerEra: session::SessionIndex = 6;
|
||||||
pub const BondingDuration: staking::EraIndex = 24 * 28;
|
pub const BondingDuration: staking::EraIndex = 24 * 28;
|
||||||
|
pub const AttestationPeriod: u64 = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl staking::Trait for Test {
|
impl staking::Trait for Test {
|
||||||
@@ -964,6 +1005,11 @@ mod tests {
|
|||||||
type Time = timestamp::Module<Test>;
|
type Time = timestamp::Module<Test>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl attestations::Trait for Test {
|
||||||
|
type AttestationPeriod = AttestationPeriod;
|
||||||
|
type ValidatorIdentities = ValidatorIdentities<Test>;
|
||||||
|
}
|
||||||
|
|
||||||
impl Trait for Test {
|
impl Trait for Test {
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Call = Call;
|
type Call = Call;
|
||||||
@@ -974,7 +1020,9 @@ mod tests {
|
|||||||
type System = system::Module<Test>;
|
type System = system::Module<Test>;
|
||||||
|
|
||||||
fn new_test_ext(parachains: Vec<(ParaId, Vec<u8>, Vec<u8>)>) -> TestExternalities<Blake2Hasher> {
|
fn new_test_ext(parachains: Vec<(ParaId, Vec<u8>, Vec<u8>)>) -> TestExternalities<Blake2Hasher> {
|
||||||
let mut t = system::GenesisConfig::default().build_storage::<Test>().unwrap().0;
|
use staking::StakerStatus;
|
||||||
|
|
||||||
|
let (mut t, mut c) = system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||||
let authority_keys = [
|
let authority_keys = [
|
||||||
Ed25519Keyring::Alice,
|
Ed25519Keyring::Alice,
|
||||||
Ed25519Keyring::Bob,
|
Ed25519Keyring::Bob,
|
||||||
@@ -986,16 +1034,51 @@ mod tests {
|
|||||||
Ed25519Keyring::Two,
|
Ed25519Keyring::Two,
|
||||||
];
|
];
|
||||||
|
|
||||||
t.extend(session::GenesisConfig::<Test>{
|
// stashes are the index.
|
||||||
keys: vec![(1, UintAuthorityId(1))],
|
let session_keys: Vec<_> = authority_keys.iter().enumerate()
|
||||||
}.build_storage().unwrap().0);
|
.map(|(i, _k)| (i as u64, UintAuthorityId(i as u64)))
|
||||||
t.extend(GenesisConfig::<Test>{
|
.collect();
|
||||||
|
|
||||||
|
let authorities: Vec<_> = authority_keys.iter().map(|k| SessionKey::from(*k)).collect();
|
||||||
|
|
||||||
|
// controllers are the index + 1000
|
||||||
|
let stakers: Vec<_> = (0..authority_keys.len()).map(|i| (
|
||||||
|
i as u64,
|
||||||
|
i as u64 + 1000,
|
||||||
|
10_000,
|
||||||
|
StakerStatus::<u64>::Validator,
|
||||||
|
)).collect();
|
||||||
|
|
||||||
|
let balances: Vec<_> = (0..authority_keys.len()).map(|i| (i as u64, 10_000_000)).collect();
|
||||||
|
|
||||||
|
session::GenesisConfig::<Test> {
|
||||||
|
keys: session_keys,
|
||||||
|
}.assimilate_storage(&mut t, &mut c).unwrap();
|
||||||
|
GenesisConfig::<Test> {
|
||||||
parachains,
|
parachains,
|
||||||
|
authorities: authorities.clone(),
|
||||||
_phdata: Default::default(),
|
_phdata: Default::default(),
|
||||||
}.build_storage().unwrap().0);
|
}.assimilate_storage(&mut t, &mut c).unwrap();
|
||||||
t.extend(aura::GenesisConfig::<Test>{
|
|
||||||
authorities: authority_keys.iter().map(|k| SessionKey::from(*k)).collect(),
|
aura::GenesisConfig::<Test> {
|
||||||
}.build_storage().unwrap().0);
|
authorities,
|
||||||
|
}.assimilate_storage(&mut t, &mut c).unwrap();
|
||||||
|
|
||||||
|
balances::GenesisConfig::<Test> {
|
||||||
|
balances,
|
||||||
|
vesting: vec![],
|
||||||
|
}.assimilate_storage(&mut t, &mut c).unwrap();
|
||||||
|
|
||||||
|
staking::GenesisConfig::<Test> {
|
||||||
|
current_era: 0,
|
||||||
|
stakers,
|
||||||
|
validator_count: 10,
|
||||||
|
minimum_validator_count: 8,
|
||||||
|
offline_slash: Perbill::from_percent(5),
|
||||||
|
offline_slash_grace: 0,
|
||||||
|
invulnerables: vec![],
|
||||||
|
}.assimilate_storage(&mut t, &mut c).unwrap();
|
||||||
|
|
||||||
t.into()
|
t.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,10 +1090,10 @@ mod tests {
|
|||||||
let mut vote_implicit = false;
|
let mut vote_implicit = false;
|
||||||
let parent_hash = crate::System::parent_hash();
|
let parent_hash = crate::System::parent_hash();
|
||||||
|
|
||||||
let duty_roster = Parachains::calculate_duty_roster();
|
let (duty_roster, _) = Parachains::calculate_duty_roster();
|
||||||
let candidate_hash = candidate.candidate.hash();
|
let candidate_hash = candidate.candidate.hash();
|
||||||
|
|
||||||
let authorities = crate::Aura::authorities();
|
let authorities = Parachains::authorities();
|
||||||
let extract_key = |public: SessionKey| {
|
let extract_key = |public: SessionKey| {
|
||||||
Ed25519Keyring::from_raw_public(public.0).unwrap()
|
Ed25519Keyring::from_raw_public(public.0).unwrap()
|
||||||
};
|
};
|
||||||
@@ -1407,17 +1490,17 @@ mod tests {
|
|||||||
assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
|
assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
let duty_roster_0 = Parachains::calculate_duty_roster();
|
let duty_roster_0 = Parachains::calculate_duty_roster().0;
|
||||||
check_roster(&duty_roster_0);
|
check_roster(&duty_roster_0);
|
||||||
|
|
||||||
System::initialize(&1, &H256::from([1; 32]), &Default::default(), &Default::default());
|
System::initialize(&1, &H256::from([1; 32]), &Default::default(), &Default::default());
|
||||||
let duty_roster_1 = Parachains::calculate_duty_roster();
|
let duty_roster_1 = Parachains::calculate_duty_roster().0;
|
||||||
check_roster(&duty_roster_1);
|
check_roster(&duty_roster_1);
|
||||||
assert!(duty_roster_0 != duty_roster_1);
|
assert!(duty_roster_0 != duty_roster_1);
|
||||||
|
|
||||||
|
|
||||||
System::initialize(&2, &H256::from([2; 32]), &Default::default(), &Default::default());
|
System::initialize(&2, &H256::from([2; 32]), &Default::default(), &Default::default());
|
||||||
let duty_roster_2 = Parachains::calculate_duty_roster();
|
let duty_roster_2 = Parachains::calculate_duty_roster().0;
|
||||||
check_roster(&duty_roster_2);
|
check_roster(&duty_roster_2);
|
||||||
assert!(duty_roster_0 != duty_roster_2);
|
assert!(duty_roster_0 != duty_roster_2);
|
||||||
assert!(duty_roster_1 != duty_roster_2);
|
assert!(duty_roster_1 != duty_roster_2);
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ impl<C, TxApi> CreateProposal<C, TxApi> where
|
|||||||
let mut inherent_data = self.inherent_data
|
let mut inherent_data = self.inherent_data
|
||||||
.take()
|
.take()
|
||||||
.expect("CreateProposal is not polled after finishing; qed");
|
.expect("CreateProposal is not polled after finishing; qed");
|
||||||
inherent_data.put_data(polkadot_runtime::PARACHAIN_INHERENT_IDENTIFIER, &candidates).map_err(Error::InherentError)?;
|
inherent_data.put_data(polkadot_runtime::NEW_HEADS_IDENTIFIER, &candidates).map_err(Error::InherentError)?;
|
||||||
|
|
||||||
let runtime_api = self.client.runtime_api();
|
let runtime_api = self.client.runtime_api();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user