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 bitvec::BigEndian;
use sr_primitives::traits::{Hash as HashT, BlakeTwo256};
use primitives::parachain::{Id as ParaId, Chain, DutyRoster, AttestedCandidate, Statement};
use {system, session};
use srml_support::{StorageValue, StorageMap};
use srml_support::dispatch::Result;
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;
pub trait Trait: session::Trait {}
// 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
];
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>;
// Did the parachain heads get updated in this block?
DidUpdate: bool;
}
add_extra_genesis {
config(parachains): Vec<(ParaId, Vec<u8>, Vec<u8>)>;
build(|storage: &mut StorageOverlay, _: &mut ChildrenStorageOverlay, config: &GenesisConfig<T>| {
use codec::Encode;
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();
storage.insert(Self::hash(<Parachains<T>>::key()).to_vec(), only_ids.encode());
let code_key = Self::hash(&<Code<T>>::key_for(&id)).to_vec();
let head_key = Self::hash(&<Heads<T>>::key_for(&id)).to_vec();
// no ingress -- a chain cannot be routed to until it is live.
storage.insert(code_key, code.encode());
storage.insert(head_key, genesis.encode());
}
});
}
}
decl_module! {
/// Parachains module.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// Provide candidate receipts for parachains, in ascending order by id.
fn set_heads(origin, heads: Vec<AttestedCandidate>) -> Result {
ensure_inherent(origin)?;
ensure!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
let active_parachains = Self::active_parachains();
// perform integrity checks before writing to storage.
{
let n_parachains = active_parachains.len();
ensure!(heads.len() <= n_parachains, "Too many parachain candidates");
let mut last_id = None;
let mut iter = active_parachains.iter();
for head in &heads {
// proposed heads must be ascending order by parachain ID without duplicate.
ensure!(
last_id.as_ref().map_or(true, |x| x < &head.parachain_index()),
"Parachain candidates out of order by ID"
);
// must be unknown since active parachains are always sorted.
ensure!(
iter.find(|x| x == &&head.parachain_index()).is_some(),
"Submitted candidate for unregistered or out-of-order parachain {}"
);
Self::check_egress_queue_roots(&head, &active_parachains)?;
last_id = Some(head.parachain_index());
Self::check_attestations(&heads)?;
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);
}
}
<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 {
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(())
}
/// Deregister a parachain with given id
pub fn deregister_parachain(id: ParaId) -> Result {
let mut parachains = Self::active_parachains();
match parachains.binary_search(&id) {
Ok(idx) => { parachains.remove(idx); }
}
<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));
}
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> {
use codec::Encode;
let mut encoded = statement.encode();
encoded.extend(parent_hash.as_ref());
encoded
}
impl<T: Trait> Module<T> {
/// 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 random_seed = system::Module::<T>::random_seed().as_ref().to_vec();
random_seed.extend(b"validator_role_pairs");
let mut seed = BlakeTwo256::hash(&random_seed);
// 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())
}
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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)]);
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
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()];
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
for (auth_id, validity_attestation) in &candidate.validity_votes {
// protect against double-votes.
match validator_group.iter().find(|&(idx, _)| &authorities[*idx] == auth_id) {
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!(
"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 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}, SessionKey};
use keyring::{AuthorityKeyring, AccountKeyring};
use {consensus, timestamp};
impl_outer_origin! {
pub enum Origin for Test {}
}
pub struct Test;
impl consensus::Trait for Test {
type SessionKey = SessionKey;
type Log = ::Log;
}
impl system::Trait for Test {
type Origin = Origin;
type BlockNumber = u64;
type Hash = H256;
type Digest = generic::Digest<::Log>;
type AccountId = ::AccountId;
type Header = ::Header;
type Log = ::Log;
}
impl session::Trait for Test {
type OnSessionChange = ();
}
impl timestamp::Trait for Test {
type Moment = u64;
type Parachains = 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| ::AccountId::from(*k)).collect(),
t.extend(GenesisConfig::<Test>{
fn make_attestations(candidate: &mut AttestedCandidate) {
let mut vote_implicit = false;
let parent_hash = ::System::parent_hash();
let duty_roster = Parachains::calculate_duty_roster();
let candidate_hash = candidate.candidate.hash();
let authorities = ::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((authorities[idx].clone(), 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]),
balance_uploads: vec![],
egress_queue_roots,
fees: 0,
block_data_hash: Default::default(),
}
}
}
fn active_parachains_should_work() {
let parachains = vec![
asynchronous rob
committed
(5u32.into(), vec![1,2,3], vec![1]),
(100u32.into(), vec![4,5,6], vec![2]),
];
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![
asynchronous rob
committed
(5u32.into(), vec![1,2,3], vec![1]),
(100u32.into(), vec![4,5,6], vec![2,]),
];
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]));
assert_ok!(Parachains::register_parachain(99u32.into(), vec![7,8,9], vec![1, 1, 1]));
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]);
assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9]));
assert_ok!(Parachains::deregister_parachain(5u32.into()));
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![
asynchronous rob
committed
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], 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.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3);
}
assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
};
system::Module::<Test>::set_random_seed([0u8; 32].into());
let duty_roster_0 = Parachains::calculate_duty_roster();
check_roster(&duty_roster_0);
system::Module::<Test>::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::<Test>::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);
});
}
#[test]
fn unattested_candidate_is_rejected() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
let candidate = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
};
assert!(Parachains::dispatch(Call::set_heads(vec![candidate]), Origin::INHERENT).is_err());
})
}
#[test]
fn attested_candidates_accepted_in_order() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
let mut candidate_a = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
}
};
let mut candidate_b = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 1.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![2, 3, 4]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
}
};
make_attestations(&mut candidate_a);
make_attestations(&mut candidate_b);
assert!(Parachains::dispatch(
Call::set_heads(vec![candidate_b.clone(), candidate_a.clone()]),
Origin::INHERENT,
).is_err());
assert!(Parachains::dispatch(
Call::set_heads(vec![candidate_a.clone(), candidate_b.clone()]),
Origin::INHERENT,
).is_ok());
});
}
#[test]
fn duplicate_vote_is_rejected() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
let mut candidate = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
}
};
make_attestations(&mut candidate);
let mut double_validity = candidate.clone();
double_validity.validity_votes.push(candidate.validity_votes[0].clone());
assert!(Parachains::dispatch(
Call::set_heads(vec![double_validity]),
Origin::INHERENT,
).is_err());
});
}
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
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
#[test]
fn ingress_works() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
(99u32.into(), vec![1, 2, 3], vec![4, 5, 6]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
let from_a = vec![(1.into(), [1; 32].into())];
let mut candidate_a = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: from_a.clone(),
fees: 0,
block_data_hash: Default::default(),
}
};
let from_b = vec![(99.into(), [1; 32].into())];
let mut candidate_b = AttestedCandidate {
validity_votes: vec![],
candidate: CandidateReceipt {
parachain_index: 1.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
balance_uploads: vec![],
egress_queue_roots: from_b.clone(),
fees: 0,
block_data_hash: Default::default(),
}
};
make_attestations(&mut candidate_a);
make_attestations(&mut candidate_b);
assert_eq!(Parachains::ingress(ParaId::from(1)), Some(Vec::new()));
assert_eq!(Parachains::ingress(ParaId::from(99)), Some(Vec::new()));
assert!(Parachains::dispatch(
Call::set_heads(vec![candidate_a, candidate_b]),
Origin::INHERENT,
).is_ok());
assert_eq!(
Parachains::ingress(ParaId::from(1)),
Some(vec![(0.into(), [1; 32].into())]),
);
assert_eq!(
Parachains::ingress(ParaId::from(99)),
Some(vec![(1.into(), [1; 32].into())]),
);
assert_ok!(Parachains::deregister_parachain(1u32.into()));
// after deregistering, there is no ingress to 1 and we stop routing
// from 1.
assert_eq!(Parachains::ingress(ParaId::from(1)), None);
assert_eq!(Parachains::ingress(ParaId::from(99)), Some(Vec::new()));
});
}
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
#[test]
fn egress_routed_to_non_existent_parachain_is_rejected() {
// That no parachain is routed to which doesn't exist
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
// parachain 99 does not exist
let non_existent = vec![(99.into(), [1; 32].into())];
let mut candidate = new_candidate_with_egress_roots(non_existent);
make_attestations(&mut candidate);
let result = Parachains::dispatch(
Call::set_heads(vec![candidate.clone()]),
Origin::INHERENT,
);
assert_eq!(Err("Routing to non-existent parachain"), result);
});
}
#[test]
fn egress_routed_to_self_is_rejected() {
// That the parachain doesn't route to self
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
// parachain 0 is self
let to_self = vec![(0.into(), [1; 32].into())];
let mut candidate = new_candidate_with_egress_roots(to_self);
make_attestations(&mut candidate);
let result = Parachains::dispatch(
Call::set_heads(vec![candidate.clone()]),
Origin::INHERENT,
);
assert_eq!(Err("Parachain routing to self"), result);
});
}
#[test]
fn egress_queue_roots_out_of_order_rejected() {
// That the list of egress queue roots is in ascending order by `ParaId`.
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
// parachain 0 is self
let out_of_order = vec![(1.into(), [1; 32].into()), ((0.into(), [1; 32].into()))];
let mut candidate = new_candidate_with_egress_roots(out_of_order);
make_attestations(&mut candidate);
let result = Parachains::dispatch(
Call::set_heads(vec![candidate.clone()]),
Origin::INHERENT,
);
assert_eq!(Err("Egress routes out of order by ID"), result);
});
}
#[test]
fn egress_queue_roots_empty_trie_roots_rejected() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
(2u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
system::Module::<Test>::set_random_seed([0u8; 32].into());
// parachain 0 is self
let contains_empty_trie_root = vec![(1.into(), [1; 32].into()), ((2.into(), EMPTY_TRIE_ROOT.into()))];
let mut candidate = new_candidate_with_egress_roots(contains_empty_trie_root);
make_attestations(&mut candidate);
let result = Parachains::dispatch(
Call::set_heads(vec![candidate.clone()]),
Origin::INHERENT,
);
assert_eq!(Err("Empty trie root included"), result);
});
}
#[test]
fn empty_trie_root_const_is_blake2_hashed_null_node() {
let hashed_null_node = <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node();
assert_eq!(hashed_null_node, EMPTY_TRIE_ROOT.into())
}