mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 18:41:03 +00:00
Minimal parachain framework part 1 (#113)
* dynamic inclusion threshold calculator * collators interface * collation helpers * initial proposal-creation future * create proposer when asked to propose * remove local_availability duty * statement table tracks includable parachain count * beginnings of timing future * finish proposal logic * remove stray println * extract shared table to separate module * change ordering * includability tracking * fix doc * initial changes to parachains module * initialise dummy block before API calls * give polkadot control over round proposer based on random seed * propose only after enough candidates * flesh out parachains module a bit more * set_heads * actually introduce set_heads to runtime * update block_builder to accept parachains * split block validity errors from real errors in evaluation * update WASM runtimes * polkadot-api methods for parachains additions * delay evaluation until candidates are ready * comments * fix dynamic inclusion with zero initial * test for includability tracker * wasm validation of parachain candidates * move primitives to primitives crate * remove runtime-std dependency from codec * adjust doc * polkadot-parachain-primitives * kill legacy polkadot-validator crate * basic-add test chain * test for basic_add parachain * move to test-chains dir * use wasm-build * new wasm directory layout * reorganize a bit more * Fix for rh-minimal-parachain (#141) * Remove extern "C" We already encountered such behavior (bug?) in pwasm-std, I believe. * Fix `panic_fmt` signature by adding `_col` Wrong `panic_fmt` signature can inhibit some optimizations in LTO mode. * Add linker flags and use wasm-gc in build script Pass --import-memory to LLD to emit wasm binary with imported memory. Also use wasm-gc instead of wasm-build. * Fix effective_max. I'm not sure why it was the way it was actually. * Recompile wasm. * Fix indent * more basic_add tests * validate parachain WASM * produce statements on receiving statements * tests for reactive statement production * fix build * add OOM lang item to runtime-io * use dynamic_inclusion when evaluating as well * fix update_includable_count * remove dead code * grumbles * actually defer round_proposer logic * update wasm * address a few more grumbles * grumbles * update WASM checkins * remove dependency on tokio-timer
This commit is contained in:
committed by
GitHub
parent
59e8adb604
commit
c473c0c734
@@ -17,44 +17,70 @@
|
||||
//! Main parachains logic. For now this is just the determination of which validators do what.
|
||||
|
||||
use polkadot_primitives;
|
||||
#[cfg(any(feature = "std", test))] use {runtime_io, runtime_primitives};
|
||||
use rstd::prelude::*;
|
||||
#[cfg(any(feature = "std", test))] use rstd::marker::PhantomData;
|
||||
use codec::{Slicable, Joiner};
|
||||
use runtime_support::Hashable;
|
||||
#[cfg(any(feature = "std", test))] use runtime_support::StorageValue;
|
||||
use runtime_primitives::traits::Executable;
|
||||
use polkadot_primitives::parachain::{Id, Chain, DutyRoster};
|
||||
|
||||
use runtime_primitives::traits::{Executable, RefInto, MaybeEmpty};
|
||||
use polkadot_primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt};
|
||||
use {system, session};
|
||||
|
||||
pub trait Trait: system::Trait<Hash = polkadot_primitives::Hash> + session::Trait {}
|
||||
use runtime_support::{StorageValue, StorageMap};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use rstd::marker::PhantomData;
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use {runtime_io, runtime_primitives};
|
||||
|
||||
pub trait Trait: system::Trait<Hash = polkadot_primitives::Hash> + session::Trait {
|
||||
/// The position of the set_heads call in the block.
|
||||
const SET_POSITION: u32;
|
||||
|
||||
type PublicAux: RefInto<Self::AccountId> + MaybeEmpty;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait>;
|
||||
pub enum Call where aux: <T as Trait>::PublicAux {
|
||||
// provide candidate receipts for parachains, in ascending order by id.
|
||||
fn set_heads(aux, heads: Vec<CandidateReceipt>) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
pub trait Store for Module<T: Trait>;
|
||||
// The number of parachains registered at present.
|
||||
pub Count get(count): b"para:count" => default u32;
|
||||
trait Store for Module<T: Trait>;
|
||||
// Vector of all parachain IDs.
|
||||
pub Parachains get(active_parachains): b"para:chains" => default Vec<Id>;
|
||||
// The parachains registered at present.
|
||||
pub Code get(parachain_code): b"para:code" => map [ Id => Vec<u8> ];
|
||||
// The heads of the parachains registered at present. these are kept sorted.
|
||||
pub Heads get(parachain_head): b"para:head" => map [ Id => Vec<u8> ];
|
||||
|
||||
// Did the parachain heads get updated in this block?
|
||||
DidUpdate: b"para:did" => default bool;
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Calculate the current block's duty roster.
|
||||
/// Calculate the current block's duty roster using system's random seed.
|
||||
pub fn calculate_duty_roster() -> DutyRoster {
|
||||
let parachain_count = Self::count();
|
||||
let validator_count = <session::Module<T>>::validator_count();
|
||||
let parachains = Self::active_parachains();
|
||||
let parachain_count = parachains.len();
|
||||
let validator_count = <session::Module<T>>::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 =>
|
||||
Chain::Parachain(Id::from(i / validators_per_parachain as u32)),
|
||||
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 roles_gua = roles_val.clone();
|
||||
|
||||
let h = <system::Module<T>>::random_seed();
|
||||
let mut seed = h.to_vec().and(b"validator_role_pairs").blake2_256();
|
||||
let random_seed = system::Module::<T>::random_seed();
|
||||
let mut seed = random_seed.to_vec().and(b"validator_role_pairs").blake2_256();
|
||||
|
||||
// shuffle
|
||||
for i in 0..(validator_count - 1) {
|
||||
@@ -83,16 +109,76 @@ impl<T: Trait> Module<T> {
|
||||
guarantor_duty: roles_gua,
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a parachain with given code.
|
||||
/// Fails if given ID is already used.
|
||||
pub fn register_parachain(id: Id, code: Vec<u8>, initial_head_data: Vec<u8>) {
|
||||
let mut parachains = Self::active_parachains();
|
||||
match parachains.binary_search(&id) {
|
||||
Ok(_) => panic!("Parachain with id {} already exists", id.into_inner()),
|
||||
Err(idx) => parachains.insert(idx, id),
|
||||
}
|
||||
|
||||
<Code<T>>::insert(id, code);
|
||||
<Parachains<T>>::put(parachains);
|
||||
<Heads<T>>::insert(id, initial_head_data);
|
||||
}
|
||||
|
||||
/// Deregister a parachain with given id
|
||||
pub fn deregister_parachain(id: Id) {
|
||||
let mut parachains = Self::active_parachains();
|
||||
match parachains.binary_search(&id) {
|
||||
Ok(idx) => { parachains.remove(idx); }
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
<Code<T>>::remove(id);
|
||||
<Heads<T>>::remove(id);
|
||||
<Parachains<T>>::put(parachains);
|
||||
}
|
||||
|
||||
fn set_heads(aux: &<T as Trait>::PublicAux, heads: Vec<CandidateReceipt>) {
|
||||
assert!(aux.is_empty());
|
||||
assert!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
|
||||
assert!(
|
||||
<system::Module<T>>::extrinsic_index() == T::SET_POSITION,
|
||||
"Parachain heads update extrinsic must be at position {} in the block",
|
||||
T::SET_POSITION
|
||||
);
|
||||
|
||||
let active_parachains = Self::active_parachains();
|
||||
let mut iter = active_parachains.iter();
|
||||
|
||||
// perform this check before writing to storage.
|
||||
for head in &heads {
|
||||
assert!(
|
||||
iter.find(|&p| p == &head.parachain_index).is_some(),
|
||||
"Submitted candidate for unregistered or out-of-order parachain {}",
|
||||
head.parachain_index.into_inner()
|
||||
);
|
||||
}
|
||||
|
||||
for head in heads {
|
||||
let id = head.parachain_index.clone();
|
||||
<Heads<T>>::insert(id, head.head_data.0);
|
||||
}
|
||||
|
||||
<DidUpdate<T>>::put(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
fn execute() {
|
||||
assert!(<Self as Store>::DidUpdate::take(), "Parachain heads must be updated once in the block");
|
||||
}
|
||||
}
|
||||
|
||||
/// Parachains module genesis configuration.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub struct GenesisConfig<T: Trait> {
|
||||
pub count: u32,
|
||||
/// The initial parachains, mapped to code.
|
||||
pub parachains: Vec<(Id, Vec<u8>)>,
|
||||
/// Phantom data.
|
||||
pub phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
@@ -100,7 +186,7 @@ pub struct GenesisConfig<T: Trait> {
|
||||
impl<T: Trait> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
count: 0,
|
||||
parachains: Vec::new(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -109,12 +195,26 @@ impl<T: Trait> Default for GenesisConfig<T> {
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl<T: Trait> runtime_primitives::BuildExternalities for GenesisConfig<T>
|
||||
{
|
||||
fn build_externalities(self) -> runtime_io::TestExternalities {
|
||||
fn build_externalities(mut self) -> runtime_io::TestExternalities {
|
||||
use std::collections::HashMap;
|
||||
use runtime_io::twox_128;
|
||||
use codec::Slicable;
|
||||
map![
|
||||
twox_128(<Count<T>>::key()).to_vec() => self.count.encode()
|
||||
]
|
||||
|
||||
self.parachains.sort_unstable_by_key(|&(ref id, _)| id.clone());
|
||||
self.parachains.dedup_by_key(|&mut (ref id, _)| id.clone());
|
||||
|
||||
let only_ids: Vec<_> = self.parachains.iter().map(|&(ref id, _)| id).cloned().collect();
|
||||
|
||||
let mut map: HashMap<_, _> = map![
|
||||
twox_128(<Parachains<T>>::key()).to_vec() => only_ids.encode()
|
||||
];
|
||||
|
||||
for (id, code) in self.parachains {
|
||||
let key = twox_128(&<Code<T>>::key_for(&id)).to_vec();
|
||||
map.insert(key, code.encode());
|
||||
}
|
||||
|
||||
map.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,12 +248,15 @@ mod tests {
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = Identity;
|
||||
}
|
||||
impl Trait for Test {}
|
||||
impl Trait for Test {
|
||||
const SET_POSITION: u32 = 0;
|
||||
|
||||
type PublicAux = <Self as HasPublicAux>::PublicAux;
|
||||
}
|
||||
|
||||
type System = system::Module<Test>;
|
||||
type Parachains = Module<Test>;
|
||||
|
||||
fn new_test_ext() -> runtime_io::TestExternalities {
|
||||
fn new_test_ext(parachains: Vec<(Id, Vec<u8>)>) -> runtime_io::TestExternalities {
|
||||
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
|
||||
t.extend(consensus::GenesisConfig::<Test>{
|
||||
code: vec![],
|
||||
@@ -164,22 +267,59 @@ mod tests {
|
||||
validators: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
}.build_externalities());
|
||||
t.extend(GenesisConfig::<Test>{
|
||||
count: 2,
|
||||
parachains: parachains,
|
||||
phantom: PhantomData,
|
||||
}.build_externalities());
|
||||
t
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_setup_should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
assert_eq!(Parachains::count(), 2);
|
||||
fn active_parachains_should_work() {
|
||||
let parachains = vec![
|
||||
(5u32.into(), vec![1,2,3]),
|
||||
(100u32.into(), vec![4,5,6]),
|
||||
];
|
||||
|
||||
with_externalities(&mut new_test_ext(parachains), || {
|
||||
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]);
|
||||
assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3]));
|
||||
assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6]));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_work() {
|
||||
with_externalities(&mut new_test_ext(), || {
|
||||
fn register_deregister() {
|
||||
let parachains = vec![
|
||||
(5u32.into(), vec![1,2,3]),
|
||||
(100u32.into(), vec![4,5,6]),
|
||||
];
|
||||
|
||||
with_externalities(&mut new_test_ext(parachains), || {
|
||||
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 100u32.into()]);
|
||||
|
||||
assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3]));
|
||||
assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6]));
|
||||
|
||||
Parachains::register_parachain(99u32.into(), vec![7,8,9], vec![1, 1, 1]);
|
||||
|
||||
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]);
|
||||
assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9]));
|
||||
|
||||
Parachains::deregister_parachain(5u32.into());
|
||||
|
||||
assert_eq!(Parachains::active_parachains(), vec![99u32.into(), 100u32.into()]);
|
||||
assert_eq!(Parachains::parachain_code(&5u32.into()), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duty_roster_works() {
|
||||
let parachains = vec![
|
||||
(0u32.into(), vec![]),
|
||||
(1u32.into(), vec![]),
|
||||
];
|
||||
|
||||
with_externalities(&mut new_test_ext(parachains), || {
|
||||
let check_roster = |duty_roster: &DutyRoster| {
|
||||
assert_eq!(duty_roster.validator_duty.len(), 8);
|
||||
assert_eq!(duty_roster.guarantor_duty.len(), 8);
|
||||
@@ -191,16 +331,17 @@ mod tests {
|
||||
assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
|
||||
};
|
||||
|
||||
System::set_random_seed([0u8; 32].into());
|
||||
system::Module::<Test>::set_random_seed([0u8; 32].into());
|
||||
let duty_roster_0 = Parachains::calculate_duty_roster();
|
||||
check_roster(&duty_roster_0);
|
||||
|
||||
System::set_random_seed([1u8; 32].into());
|
||||
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::set_random_seed([2u8; 32].into());
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user