Newer
Older
// 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 <http://www.gnu.org/licenses/>.
//! Main parachains logic. For now this is just the determination of which validators do what.
use rstd::prelude::*;
use parity_codec::{Decode, HasCompact};
use srml_support::{decl_storage, decl_module, fail, ensure};
use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Member, CheckedConversion};
use primitives::{Hash, parachain::{
Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement, AccountIdConversion,
ParachainDispatchOrigin, UpwardMessage
}};
use {system, session};
use srml_support::{
StorageValue, StorageMap, storage::AppendableStorageMap, Parameter, Dispatchable, dispatch::Result
};
#[cfg(feature = "std")]
use srml_support::storage::hashed::generator;
use inherents::{ProvideInherent, InherentData, RuntimeString, MakeFatalError, InherentIdentifier};
#[cfg(any(feature = "std", test))]
use sr_primitives::{StorageOverlay, ChildrenStorageOverlay};
#[cfg(any(feature = "std", test))]
use rstd::marker::PhantomData;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/// Parachain registration API.
pub trait ParachainRegistrar<AccountId> {
/// An identifier for a parachain.
type ParaId: Member + Parameter + Default + AccountIdConversion<AccountId> + Copy + HasCompact;
/// Create a new unique parachain identity for later registration.
fn new_id() -> Self::ParaId;
/// Register a parachain with given `code` and `initial_head_data`. `id` must not yet be registered or it will
/// result in a error.
fn register_parachain(id: Self::ParaId, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result;
/// Deregister a parachain with given `id`. If `id` is not currently registered, an error is returned.
fn deregister_parachain(id: Self::ParaId) -> Result;
}
impl<T: Trait> ParachainRegistrar<T::AccountId> for Module<T> {
type ParaId = ParaId;
fn new_id() -> ParaId {
<NextFreeId<T>>::mutate(|n| { let r = *n; *n = ParaId::from(u32::from(*n) + 1); r })
}
fn register_parachain(id: ParaId, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result {
let mut parachains = Self::active_parachains();
match parachains.binary_search(&id) {
Ok(_) => fail!("Parachain already exists"),
Err(idx) => parachains.insert(idx, id),
}
<Code<T>>::insert(id, code);
<Parachains<T>>::put(parachains);
<Heads<T>>::insert(id, initial_head_data);
Ok(())
}
fn deregister_parachain(id: ParaId) -> Result {
let mut parachains = Self::active_parachains();
match parachains.binary_search(&id) {
Ok(idx) => { parachains.remove(idx); }
Err(_) => return Ok(()),
}
<Code<T>>::remove(id);
<Heads<T>>::remove(id);
// clear all routing entries to and from other parachains.
for other in parachains.iter().cloned() {
<Routing<T>>::remove((id, other));
<Routing<T>>::remove((other, id));
}
<Parachains<T>>::put(parachains);
Ok(())
}
}
pub trait Trait: session::Trait {
/// The outer origin type.
type Origin: From<Origin> + From<system::RawOrigin<Self::AccountId>>;
/// The outer call dispatch type.
type Call: Parameter + Dispatchable<Origin=<Self as Trait>::Origin>;
}
/// Origin for the parachains module.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Origin {
/// It comes from a parachain.
Parachain(ParaId),
// result of <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node()
const EMPTY_TRIE_ROOT: [u8; 32] = [
3, 23, 10, 46, 117, 151, 183, 183, 227, 216, 76, 5, 57, 29, 19, 154,
98, 177, 87, 231, 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, 19, 20
];
/// Total number of individual messages allowed in the parachain -> relay-chain message queue.
const MAX_QUEUE_COUNT: usize = 100;
/// Total size of messages allowed in the parachain -> relay-chain message queue before which no
/// further messages may be added to it. If it exceeds this then the queue may contain only a
/// single message.
const WATERMARK_QUEUE_SIZE: usize = 20000;
trait Store for Module<T: Trait> as Parachains {
// Vector of all parachain IDs.
pub Parachains get(active_parachains): Vec<ParaId>;
pub Code get(parachain_code): map ParaId => Option<Vec<u8>>;
// The heads of the parachains registered at present.
pub Heads get(parachain_head): map ParaId => Option<Vec<u8>>;
// message routing roots (from, to).
pub Routing: map (ParaId, ParaId) => Option<Hash>;
/// Messages ready to be dispatched onto the relay chain. It is subject to
/// `MAX_MESSAGE_COUNT` and `WATERMARK_MESSAGE_SIZE`.
pub RelayDispatchQueue: map ParaId => Vec<UpwardMessage>;
/// 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
// second if the total length (in bytes) of the message payloads.
pub RelayDispatchQueueSize: map ParaId => (u32, u32);
// Did the parachain heads get updated in this block?
/// The next unused ParaId value.
NextFreeId: ParaId;
config(parachains): Vec<(ParaId, Vec<u8>, Vec<u8>)>;
build(|storage: &mut StorageOverlay, _: &mut ChildrenStorageOverlay, config: &GenesisConfig<T>| {
let mut p = config.parachains.clone();
p.sort_unstable_by_key(|&(ref id, _, _)| id.clone());
p.dedup_by_key(|&mut (ref id, _, _)| id.clone());
let only_ids: Vec<_> = p.iter().map(|&(ref id, _, _)| id).cloned().collect();
<Parachains<T> as generator::StorageValue<_>>::put(&only_ids, storage);
// no ingress -- a chain cannot be routed to until it is live.
<Code<T> as generator::StorageMap<_, _>>::insert(&id, &code, storage);
<Heads<T> as generator::StorageMap<_, _>>::insert(&id, &genesis, storage);
}
});
}
}
decl_module! {
/// Parachains 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 set_heads(origin, heads: Vec<AttestedCandidate>) -> Result {
ensure!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
let active_parachains = Self::active_parachains();
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
let parachain_count = active_parachains.len();
ensure!(heads.len() <= parachain_count, "Too many parachain candidates");
if !active_parachains.is_empty() {
// perform integrity checks before writing to storage.
{
let mut last_id = None;
let mut iter = active_parachains.iter();
for head in &heads {
let id = head.parachain_index();
// proposed heads must be ascending order by parachain ID without duplicate.
ensure!(
last_id.as_ref().map_or(true, |x| x < &id),
"Parachain candidates out of order by ID"
);
// must be unknown since active parachains are always sorted.
ensure!(
iter.find(|x| x == &&id).is_some(),
"Submitted candidate for unregistered or out-of-order parachain {}"
);
Self::check_upward_messages(
id,
&head.candidate.upward_messages,
MAX_QUEUE_COUNT,
WATERMARK_QUEUE_SIZE,
)?;
Self::check_egress_queue_roots(&head, &active_parachains)?;
last_id = Some(head.parachain_index());
}
Self::check_attestations(&heads)?;
for head in heads.iter() {
let id = head.parachain_index();
<Heads<T>>::insert(id, &head.candidate.head_data.0);
// update egress.
for &(to, root) in &head.candidate.egress_queue_roots {
<Routing<T>>::insert((id, to), root);
}
// Queue up upwards messages (from parachains to relay chain).
Self::queue_upward_messages(id, &head.candidate.upward_messages);
Self::dispatch_upward_messages(
<system::Module<T>>::block_number(),
&active_parachains,
MAX_QUEUE_COUNT,
WATERMARK_QUEUE_SIZE,
Self::dispatch_message,
);
}
<DidUpdate<T>>::put(true);
Ok(())
}
/// Register a parachain with given code.
/// Fails if given ID is already used.
pub fn register_parachain(id: ParaId, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result {
<Self as ParachainRegistrar<T::AccountId>>::register_parachain(id, code, initial_head_data)
}
/// Deregister a parachain with given id
pub fn deregister_parachain(id: ParaId) -> Result {
<Self as ParachainRegistrar<T::AccountId>>::deregister_parachain(id)
assert!(<Self as Store>::DidUpdate::take(), "Parachain heads must be updated once in the block");
}
fn majority_of(list_len: usize) -> usize {
list_len / 2 + list_len % 2
}
fn localized_payload(statement: Statement, parent_hash: ::primitives::Hash) -> Vec<u8> {
let mut encoded = statement.encode();
encoded.extend(parent_hash.as_ref());
encoded
}
impl<T: Trait> Module<T> {
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/// Dispatch some messages from a parachain.
fn dispatch_message(
id: ParaId,
origin: ParachainDispatchOrigin,
data: &[u8],
) {
if let Some(message_call) = T::Call::decode(&mut &data[..]) {
let origin: <T as Trait>::Origin = match origin {
ParachainDispatchOrigin::Signed =>
system::RawOrigin::Signed(id.into_account()).into(),
ParachainDispatchOrigin::Parachain =>
Origin::Parachain(id).into(),
};
let _ok = message_call.dispatch(origin).is_ok();
// Not much to do with the result as it is. It's up to the parachain to ensure that the
// message makes sense.
}
}
/// Ensure all is well with the upward messages.
fn check_upward_messages(
id: ParaId,
upward_messages: &[UpwardMessage],
max_queue_count: usize,
watermark_queue_size: usize,
) -> Result {
// Either there are no more messages to add...
if !upward_messages.is_empty() {
let (count, size) = <RelayDispatchQueueSize<T>>::get(id);
ensure!(
// ...or we are appending one message onto an empty queue...
upward_messages.len() + count as usize == 1
// ...or...
|| (
// ...the total messages in the queue ends up being no greater than the
// limit...
upward_messages.len() + count as usize <= max_queue_count
&&
// ...and the total size of the payloads in the queue ends up being no
// greater than the limit.
upward_messages.iter()
.fold(size as usize, |a, x| a + x.data.len())
<= watermark_queue_size
),
"Messages added when queue full"
);
}
Ok(())
}
/// Place any new upward messages into our queue for later dispatch.
fn queue_upward_messages(id: ParaId, upward_messages: &[UpwardMessage]) {
if !upward_messages.is_empty() {
<RelayDispatchQueueSize<T>>::mutate(id, |&mut(ref mut count, ref mut len)| {
*count += upward_messages.len() as u32;
*len += upward_messages.iter()
.fold(0, |a, x| a + x.data.len()) as u32;
});
// Should never be able to fail assuming our state is uncorrupted, but best not
// to panic, even if it does.
let _ = <RelayDispatchQueue<T>>::append(id, upward_messages);
}
}
/// Simple round-robin dispatcher, using block number modulo parachain count
/// to decide which takes precedence and proceeding from there.
fn dispatch_upward_messages(
now: T::BlockNumber,
active_parachains: &[ParaId],
max_queue_count: usize,
watermark_queue_size: usize,
mut dispatch_message: impl FnMut(ParaId, ParachainDispatchOrigin, &[u8]),
) {
let para_count = active_parachains.len();
let offset = (now % T::BlockNumber::from(para_count as u32))
.checked_into::<usize>()
.expect("value is modulo a usize value; qed");
let mut dispatched_count = 0usize;
let mut dispatched_size = 0usize;
for id in active_parachains.iter().cycle().skip(offset).take(para_count) {
let (count, size) = <RelayDispatchQueueSize<T>>::get(id);
let count = count as usize;
let size = size as usize;
if dispatched_count == 0 || (
dispatched_count + count <= max_queue_count
&& dispatched_size + size <= watermark_queue_size
) {
if count > 0 {
// still dispatching messages...
<RelayDispatchQueueSize<T>>::remove(id);
let messages = <RelayDispatchQueue<T>>::take(id);
for UpwardMessage { origin, data } in messages.into_iter() {
dispatch_message(*id, origin, &data);
}
dispatched_count += count;
dispatched_size += size;
if dispatched_count >= max_queue_count
|| dispatched_size >= watermark_queue_size
{
break
}
}
}
}
}
/// 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 = <consensus::Module<T>>::authorities().len();
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::<Vec<_>>();
let mut seed = {
let phrase = b"validator_role_pairs";
let seed = system::Module::<T>::random(&phrase[..]);
let seed_len = seed.as_ref().len();
let needed_bytes = validator_count * 4;
// hash only the needed bits of the random seed.
// if earlier bits are influencable, they will not factor into
// the seed used here.
let seed_off = if needed_bytes >= seed_len {
0
} else {
seed_len - needed_bytes
};
BlakeTwo256::hash(&seed.as_ref()[seed_off..])
};
// shuffle
for i in 0..(validator_count - 1) {
// 4 bytes of entropy used per cycle, 32 bytes entropy per hash
let offset = (i * 4 % 32) as usize;
// number of roles remaining to select from.
let remaining = (validator_count - i) as usize;
let val_index = u32::decode(&mut &seed[offset..offset + 4])
.expect("using 4 bytes for a 32-bit quantity") as usize % remaining;
if offset == 28 {
// into the last 4 bytes - rehash to gather new entropy
seed = BlakeTwo256::hash(seed.as_ref());
}
// exchange last item with randomly chosen first.
roles_val.swap(remaining - 1, val_index);
}
DutyRoster {
validator_duty: roles_val,
}
}
/// Calculate the ingress to a specific parachain.
///
/// Yields a list of parachains being routed from, and the egress
/// queue roots to consider.
pub fn ingress(to: ParaId) -> Option<Vec<(ParaId, Hash)>> {
let active_parachains = Self::active_parachains();
if !active_parachains.contains(&to) { return None }
Some(active_parachains.into_iter().filter(|i| i != &to)
.filter_map(move |from| {
<Routing<T>>::get((from, to.clone())).map(move |h| (from, h))
})
.collect())
}
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
fn check_egress_queue_roots(head: &AttestedCandidate, active_parachains: &[ParaId]) -> Result {
let mut last_egress_id = None;
let mut iter = active_parachains.iter();
for (egress_para_id, root) in &head.candidate.egress_queue_roots {
// egress routes should be ascending order by parachain ID without duplicate.
ensure!(
last_egress_id.as_ref().map_or(true, |x| x < &egress_para_id),
"Egress routes out of order by ID"
);
// a parachain can't route to self
ensure!(
*egress_para_id != head.candidate.parachain_index,
"Parachain routing to self"
);
// no empty trie roots
ensure!(
*root != EMPTY_TRIE_ROOT.into(),
"Empty trie root included"
);
// can't route to a parachain which doesn't exist
ensure!(
iter.find(|x| x == &egress_para_id).is_some(),
"Routing to non-existent parachain"
);
last_egress_id = Some(egress_para_id)
}
Ok(())
}
// check the attestations on these candidates. The candidates should have been checked
// that each candidates' chain ID is valid.
fn check_attestations(attested_candidates: &[AttestedCandidate]) -> Result {
use primitives::parachain::ValidityAttestation;
use sr_primitives::traits::Verify;
// returns groups of slices that have the same chain ID.
// assumes the inner slice is sorted by id.
struct GroupedDutyIter<'a> {
next_idx: usize,
}
impl<'a> GroupedDutyIter<'a> {
fn new(inner: &'a [(usize, ParaId)]) -> Self {
GroupedDutyIter { next_idx: 0, inner }
}
fn group_for(&mut self, wanted_id: ParaId) -> Option<&'a [(usize, ParaId)]> {
while let Some((id, keys)) = self.next() {
if wanted_id == id {
return Some(keys)
}
}
None
}
}
impl<'a> Iterator for GroupedDutyIter<'a> {
type Item = (ParaId, &'a [(usize, ParaId)]);
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
fn next(&mut self) -> Option<Self::Item> {
if self.next_idx == self.inner.len() { return None }
let start_idx = self.next_idx;
self.next_idx += 1;
let start_id = self.inner[start_idx].1;
while self.inner.get(self.next_idx).map_or(false, |&(_, ref id)| id == &start_id) {
self.next_idx += 1;
}
Some((start_id, &self.inner[start_idx..self.next_idx]))
}
}
let authorities = super::Consensus::authorities();
let duty_roster = Self::calculate_duty_roster();
// convert a duty roster, which is originally a Vec<Chain>, where each
// item corresponds to the same position in the session keys, into
// a list containing (index, parachain duty) where indices are into the session keys.
// this list is sorted ascending by parachain duty, just like the
// parachain candidates are.
let make_sorted_duties = |duty: &[Chain]| {
let mut sorted_duties = Vec::with_capacity(duty.len());
for (val_idx, duty) in duty.iter().enumerate() {
let id = match duty {
Chain::Relay => continue,
Chain::Parachain(id) => id,
};
let idx = sorted_duties.binary_search_by_key(&id, |&(_, ref id)| id)
.unwrap_or_else(|idx| idx);
sorted_duties.insert(idx, (val_idx, *id));
}
sorted_duties
};
let sorted_validators = make_sorted_duties(&duty_roster.validator_duty);
let parent_hash = super::System::parent_hash();
let localized_payload = |statement: Statement| localized_payload(statement, parent_hash);
let mut validator_groups = GroupedDutyIter::new(&sorted_validators[..]);
for candidate in attested_candidates {
let validator_group = validator_groups.group_for(candidate.parachain_index())
.ok_or("no validator group for parachain")?;
ensure!(
candidate.validity_votes.len() >= majority_of(validator_group.len()),
"Not enough validity attestations"
);
let mut candidate_hash = None;
let mut encoded_implicit = None;
let mut encoded_explicit = None;
// track which voters have voted already, 1 bit per authority.
let mut track_voters = bitvec![0; authorities.len()];
for (auth_index, validity_attestation) in &candidate.validity_votes {
let auth_index = *auth_index as usize;
// protect against double-votes.
match validator_group.iter().find(|&(idx, _)| *idx == auth_index) {
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
None => return Err("Attesting validator not on this chain's validation duty."),
Some(&(idx, _)) => {
if track_voters.get(idx) {
return Err("Voter already attested validity once")
}
track_voters.set(idx, true)
}
}
let (payload, sig) = match validity_attestation {
ValidityAttestation::Implicit(sig) => {
let payload = encoded_implicit.get_or_insert_with(|| localized_payload(
Statement::Candidate(candidate.candidate.clone()),
));
(payload, sig)
}
ValidityAttestation::Explicit(sig) => {
let hash = candidate_hash
.get_or_insert_with(|| candidate.candidate.hash())
.clone();
let payload = encoded_explicit.get_or_insert_with(|| localized_payload(
Statement::Valid(hash),
));
(payload, sig)
}
};
ensure!(
sig.verify(&payload[..], &authorities[auth_index]),
"Candidate validity attestation signature is bad."
);
}
}
Ok(())
}
// TODO: Consider integrating if needed. (https://github.com/paritytech/polkadot/issues/223)
/// 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.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),
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"newheads";
pub type InherentType = Vec<AttestedCandidate>;
impl<T: Trait> ProvideInherent for Module<T> {
type Call = Call<T>;
type Error = MakeFatalError<RuntimeString>;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
let data = data.get_data::<InherentType>(&INHERENT_IDENTIFIER)
.expect("Parachain heads could not be decoded.")
.expect("No parachain heads found in inherent data.");
asynchronous rob
committed
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::Call as ParachainsCall;
use sr_io::{TestExternalities, with_externalities};
use substrate_primitives::{H256, Blake2Hasher};
use sr_primitives::{generic, BuildStorage};
use sr_primitives::traits::{BlakeTwo256, IdentityLookup};
use primitives::{
parachain::{CandidateReceipt, HeadData, ValidityAttestation, ValidatorIndex}, SessionKey
};
use keyring::{AuthorityKeyring, AccountKeyring};
use srml_support::{impl_outer_origin, impl_outer_dispatch, assert_ok, assert_err};
use {consensus, timestamp};
impl_outer_origin! {
pub enum Origin for Test {
parachains
}
}
impl_outer_dispatch! {
pub enum Call for Test where origin: Origin {
parachains::Parachains,
}
pub struct Test;
impl consensus::Trait for Test {
type SessionKey = SessionKey;
}
impl system::Trait for Test {
type Origin = Origin;
type BlockNumber = u64;
type Hash = H256;
type Digest = generic::Digest<crate::Log>;
type AccountId = crate::AccountId;
type Lookup = IdentityLookup<crate::AccountId>;
type Header = crate::Header;
}
impl session::Trait for Test {
type OnSessionChange = ();
}
impl timestamp::Trait for Test {
type Moment = u64;
impl Trait for Test {
type Origin = Origin;
type Call = Call;
}
type Parachains = Module<Test>;
type System = system::Module<Test>;
fn new_test_ext(parachains: Vec<(ParaId, Vec<u8>, Vec<u8>)>) -> TestExternalities<Blake2Hasher> {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
let authority_keys = [
AuthorityKeyring::Alice,
AuthorityKeyring::Bob,
AuthorityKeyring::Charlie,
AuthorityKeyring::Dave,
AuthorityKeyring::Eve,
AuthorityKeyring::Ferdie,
AuthorityKeyring::One,
AuthorityKeyring::Two,
];
let validator_keys = [
AccountKeyring::Alice,
AccountKeyring::Bob,
AccountKeyring::Charlie,
AccountKeyring::Dave,
AccountKeyring::Eve,
AccountKeyring::Ferdie,
AccountKeyring::One,
AccountKeyring::Two,
t.extend(consensus::GenesisConfig::<Test>{
code: vec![],
authorities: authority_keys.iter().map(|k| SessionKey::from(*k)).collect(),
t.extend(session::GenesisConfig::<Test>{
session_length: 1000,
validators: validator_keys.iter().map(|k| crate::AccountId::from(*k)).collect(),
t.extend(GenesisConfig::<Test>{
fn set_heads(v: Vec<AttestedCandidate>) -> ParachainsCall<Test> {
ParachainsCall::set_heads(v)
}
fn make_attestations(candidate: &mut AttestedCandidate) {
let mut vote_implicit = false;
let parent_hash = crate::System::parent_hash();
let duty_roster = Parachains::calculate_duty_roster();
let candidate_hash = candidate.candidate.hash();
let authorities = crate::Consensus::authorities();
let extract_key = |public: SessionKey| {
AuthorityKeyring::from_raw_public(public.0).unwrap()
};
let validation_entries = duty_roster.validator_duty.iter()
for (idx, &duty) in validation_entries {
if duty != Chain::Parachain(candidate.parachain_index()) { continue }
let key = extract_key(authorities[idx].clone());
let statement = if vote_implicit {
Statement::Candidate(candidate.candidate.clone())
} else {
Statement::Valid(candidate_hash.clone())
};
let payload = localized_payload(statement, parent_hash);
let signature = key.sign(&payload[..]).into();
candidate.validity_votes.push((idx as ValidatorIndex, if vote_implicit {
ValidityAttestation::Implicit(signature)
ValidityAttestation::Explicit(signature)
}));
fn new_candidate_with_egress_roots(egress_queue_roots: Vec<(ParaId, H256)>) -> AttestedCandidate {
AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
egress_queue_roots,
fees: 0,
block_data_hash: Default::default(),
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
fn new_candidate_with_upward_messages(
id: u32,
upward_messages: Vec<(ParachainDispatchOrigin, Vec<u8>)>
) -> AttestedCandidate {
AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: id.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
upward_messages: upward_messages.into_iter()
.map(|x| UpwardMessage { origin: x.0, data: x.1 })
.collect(),
}
}
}
#[test]
fn check_dispatch_upward_works() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
(2u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains.clone()), || {
let parachains = vec![0.into(), 1.into(), 2.into()];
Parachains::queue_upward_messages(0.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![0; 4] }
]);
Parachains::queue_upward_messages(1.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1; 4] }
]);
let mut dispatched: Vec<(ParaId, ParachainDispatchOrigin, Vec<u8>)> = vec![];
let dummy = |id, origin, data: &[u8]| dispatched.push((id, origin, data.to_vec()));
Parachains::dispatch_upward_messages(0, ¶chains, 2, 3, dummy);
assert_eq!(dispatched, vec![
(0.into(), ParachainDispatchOrigin::Parachain, vec![0; 4])
]);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(0)).is_empty());
assert_eq!(<RelayDispatchQueue<Test>>::get(ParaId::from(1)).len(), 1);
});
with_externalities(&mut new_test_ext(parachains.clone()), || {
let parachains = vec![0.into(), 1.into(), 2.into()];
Parachains::queue_upward_messages(0.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![0; 2] }
]);
Parachains::queue_upward_messages(1.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1; 2] }
]);
Parachains::queue_upward_messages(2.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![2] }
]);
let mut dispatched: Vec<(ParaId, ParachainDispatchOrigin, Vec<u8>)> = vec![];
let dummy = |id, origin, data: &[u8]| dispatched.push((id, origin, data.to_vec()));
Parachains::dispatch_upward_messages(0, ¶chains, 2, 3, dummy);
assert_eq!(dispatched, vec![
(0.into(), ParachainDispatchOrigin::Parachain, vec![0; 2]),
(2.into(), ParachainDispatchOrigin::Parachain, vec![2])
]);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(0)).is_empty());
assert_eq!(<RelayDispatchQueue<Test>>::get(ParaId::from(1)).len(), 1);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(2)).is_empty());
});
with_externalities(&mut new_test_ext(parachains.clone()), || {
let parachains = vec![0.into(), 1.into(), 2.into()];
Parachains::queue_upward_messages(0.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![0; 2] }
]);
Parachains::queue_upward_messages(1.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1; 2] }
]);
Parachains::queue_upward_messages(2.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![2] }
]);
let mut dispatched: Vec<(ParaId, ParachainDispatchOrigin, Vec<u8>)> = vec![];
let dummy = |id, origin, data: &[u8]| dispatched.push((id, origin, data.to_vec()));
Parachains::dispatch_upward_messages(1, ¶chains, 2, 3, dummy);
assert_eq!(dispatched, vec![
(1.into(), ParachainDispatchOrigin::Parachain, vec![1; 2]),
(2.into(), ParachainDispatchOrigin::Parachain, vec![2])
]);
assert_eq!(<RelayDispatchQueue<Test>>::get(ParaId::from(0)).len(), 1);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(1)).is_empty());
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(2)).is_empty());
});
with_externalities(&mut new_test_ext(parachains.clone()), || {
let parachains = vec![0.into(), 1.into(), 2.into()];
Parachains::queue_upward_messages(0.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![0; 2] }
]);
Parachains::queue_upward_messages(1.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1; 2] }
]);
Parachains::queue_upward_messages(2.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![2] }
]);
let mut dispatched: Vec<(ParaId, ParachainDispatchOrigin, Vec<u8>)> = vec![];
let dummy = |id, origin, data: &[u8]| dispatched.push((id, origin, data.to_vec()));
Parachains::dispatch_upward_messages(2, ¶chains, 2, 3, dummy);
assert_eq!(dispatched, vec![
(2.into(), ParachainDispatchOrigin::Parachain, vec![2]),
(0.into(), ParachainDispatchOrigin::Parachain, vec![0; 2])
]);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(0)).is_empty());
assert_eq!(<RelayDispatchQueue<Test>>::get(ParaId::from(1)).len(), 1);
assert!(<RelayDispatchQueue<Test>>::get(ParaId::from(2)).is_empty());
});
}
#[test]
fn check_queue_upward_messages_works() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
let messages = vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0] }
];
assert_ok!(Parachains::check_upward_messages(0.into(), &messages, 2, 3));
// all good.
Parachains::queue_upward_messages(0.into(), &vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0] },
]);
let messages = vec![
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1, 2] }
];
assert_ok!(Parachains::check_upward_messages(0.into(), &messages, 2, 3));
Parachains::queue_upward_messages(0.into(), &messages);
assert_eq!(<RelayDispatchQueue<Test>>::get(ParaId::from(0)), vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0] },
UpwardMessage { origin: ParachainDispatchOrigin::Parachain, data: vec![1, 2] },
]);
});
}
#[test]
fn check_queue_full_upward_messages_fails() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
// oversize, but ok since it's just one and the queue is empty.
let messages = vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0; 4] },
];
assert_ok!(Parachains::check_upward_messages(0.into(), &messages, 2, 3));
// oversize and bad since it's not just one.
let messages = vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0] },
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0; 4] },
];
assert_err!(
Parachains::check_upward_messages(0.into(), &messages, 2, 3),
"Messages added when queue full"
);
// too many messages.
let messages = vec![
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![0] },
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![1] },
UpwardMessage { origin: ParachainDispatchOrigin::Signed, data: vec![2] },
];
assert_err!(
Parachains::check_upward_messages(0.into(), &messages, 2, 3),
"Messages added when queue full"