// Copyright 2017 Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Main parachains logic. For now this is just the determination of which validators do what. use rstd::prelude::*; use codec::Decode; use sr_primitives::{RuntimeString, traits::{Extrinsic, Block as BlockT, Hash, BlakeTwo256, ProvideInherent}}; use primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt}; use {system, session}; use srml_support::{StorageValue, StorageMap}; use srml_support::dispatch::Result; #[cfg(any(feature = "std", test))] use sr_primitives::{self, ChildrenStorageMap}; use system::ensure_inherent; pub trait Trait: session::Trait { /// The position of the set_heads call in the block. const SET_POSITION: u32; } decl_storage! { trait Store for Module as Parachains { // Vector of all parachain IDs. pub Parachains get(active_parachains): Vec; // The parachains registered at present. pub Code get(parachain_code): map Id => Option>; // The heads of the parachains registered at present. these are kept sorted. pub Heads get(parachain_head): map Id => Option>; // Did the parachain heads get updated in this block? DidUpdate: bool; } add_extra_genesis { config(parachains): Vec<(Id, Vec, Vec)>; build(|storage: &mut sr_primitives::StorageMap, _: &mut ChildrenStorageMap, config: &GenesisConfig| { 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(GenesisConfig::::hash(>::key()).to_vec(), only_ids.encode()); for (id, code, genesis) in p { let code_key = GenesisConfig::::hash(&>::key_for(&id)).to_vec(); let head_key = GenesisConfig::::hash(&>::key_for(&id)).to_vec(); storage.insert(code_key, code.encode()); storage.insert(head_key, genesis.encode()); } }); } } decl_module! { /// Parachains module. pub struct Module for enum Call where origin: T::Origin { /// Provide candidate receipts for parachains, in ascending order by id. fn set_heads(origin, heads: Vec) -> Result { ensure_inherent(origin)?; ensure!(!>::exists(), "Parachain heads must be updated only once in the block"); ensure!( >::extrinsic_index() == Some(T::SET_POSITION), "Parachain heads update extrinsic must be at position {} in the block" // , T::SET_POSITION ); 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 {}" ); last_id = Some(head.parachain_index); } } for head in heads { let id = head.parachain_index.clone(); >::insert(id, head.head_data.0); } >::put(true); Ok(()) } /// Register a parachain with given code. /// Fails if given ID is already used. pub fn register_parachain(id: Id, code: Vec, initial_head_data: Vec) -> Result { let mut parachains = Self::active_parachains(); match parachains.binary_search(&id) { Ok(_) => fail!("Parachain already exists"), Err(idx) => parachains.insert(idx, id), } >::insert(id, code); >::put(parachains); >::insert(id, initial_head_data); Ok(()) } /// Deregister a parachain with given id pub fn deregister_parachain(id: Id) -> Result { let mut parachains = Self::active_parachains(); match parachains.binary_search(&id) { Ok(idx) => { parachains.remove(idx); } Err(_) => {} } >::remove(id); >::remove(id); >::put(parachains); Ok(()) } fn on_finalise(_n: T::BlockNumber) { assert!(::DidUpdate::take(), "Parachain heads must be updated once in the block"); } } } impl Module { /// Calculate the current block's duty roster using system's random seed. pub fn calculate_duty_roster() -> DutyRoster { let parachains = Self::active_parachains(); let parachain_count = parachains.len(); let validator_count = >::validator_count() as usize; let validators_per_parachain = if parachain_count != 0 { (validator_count - 1) / parachain_count } else { 0 }; let mut roles_val = (0..validator_count).map(|i| match i { i if i < parachain_count * validators_per_parachain => { let idx = i / validators_per_parachain; Chain::Parachain(parachains[idx].clone()) } _ => Chain::Relay, }).collect::>(); let mut roles_gua = roles_val.clone(); let mut random_seed = system::Module::::random_seed().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) { // 8 bytes of entropy used per cycle, 32 bytes entropy per hash let offset = (i * 8 % 32) as usize; // number of roles remaining to select from. let remaining = (validator_count - i) as usize; // 4 * 2 32-bit ints per 256-bit seed. let val_index = u32::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; let gua_index = u32::decode(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; if offset == 24 { // into the last 8 bytes - rehash to gather new entropy seed = BlakeTwo256::hash(seed.as_ref()); } // exchange last item with randomly chosen first. roles_val.swap(remaining - 1, val_index); roles_gua.swap(remaining - 1, gua_index); } DutyRoster { validator_duty: roles_val, guarantor_duty: roles_gua, } } /* // TODO: Consider integrating if needed. /// 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), } } */ } impl ProvideInherent for Module { type Inherent = Vec; type Call = Call; type Error = RuntimeString; fn create_inherent_extrinsics(data: Self::Inherent) -> Vec<(u32, Self::Call)> { vec![(T::SET_POSITION, Call::set_heads(data))] } fn check_inherent Option<&Self::Call>>( block: &Block, _data: Self::Inherent, extract_function: &F ) -> ::rstd::result::Result<(), Self::Error> { let has_heads = block .extrinsics() .get(T::SET_POSITION as usize) .map_or(false, |xt| { xt.is_signed() == Some(true) && match extract_function(&xt) { Some(Call::set_heads(_)) => true, _ => false, } }); if !has_heads { return Err("No valid parachains inherent in block".into()) } Ok(()) } } #[cfg(test)] mod tests { use super::*; use rstd::marker::PhantomData; use sr_io::{TestExternalities, with_externalities}; use substrate_primitives::{H256, Blake2Hasher}; use sr_primitives::BuildStorage; use sr_primitives::traits::{Identity, BlakeTwo256}; use sr_primitives::testing::{Digest, Header, DigestItem}; use {consensus, timestamp}; impl_outer_origin! { pub enum Origin for Test {} } #[derive(Clone, Eq, PartialEq)] pub struct Test; impl consensus::Trait for Test { const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = u64; type OnOfflineValidator = (); type Log = DigestItem; } impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; type Digest = Digest; type AccountId = u64; type Header = Header; type Event = (); type Log = DigestItem; } impl session::Trait for Test { type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = (); type Event = (); } impl timestamp::Trait for Test { const TIMESTAMP_SET_POSITION: u32 = 0; type Moment = u64; } impl Trait for Test { const SET_POSITION: u32 = 0; } type Parachains = Module; fn new_test_ext(parachains: Vec<(Id, Vec, Vec)>) -> TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(consensus::GenesisConfig::{ code: vec![], authorities: vec![1, 2, 3], _genesis_phantom_data: PhantomData, }.build_storage().unwrap().0); t.extend(session::GenesisConfig::{ session_length: 1000, validators: vec![1, 2, 3, 4, 5, 6, 7, 8], _genesis_phantom_data: PhantomData, }.build_storage().unwrap().0); t.extend(GenesisConfig::{ parachains: parachains, _genesis_phantom_data: PhantomData, }.build_storage().unwrap().0); t.into() } #[test] fn active_parachains_should_work() { let parachains = vec![ (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![ (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![ (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.guarantor_duty.len(), 8); for i in (0..2).map(Id::from) { assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); } assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2); }; system::Module::::set_random_seed([0u8; 32].into()); let duty_roster_0 = Parachains::calculate_duty_roster(); check_roster(&duty_roster_0); system::Module::::set_random_seed([1u8; 32].into()); let duty_roster_1 = Parachains::calculate_duty_roster(); check_roster(&duty_roster_1); assert!(duty_roster_0 != duty_roster_1); system::Module::::set_random_seed([2u8; 32].into()); let duty_roster_2 = Parachains::calculate_duty_roster(); check_roster(&duty_roster_2); assert!(duty_roster_0 != duty_roster_2); assert!(duty_roster_1 != duty_roster_2); }); } }