mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 11:18:01 +00:00
Duty rester + test.
This commit is contained in:
@@ -43,7 +43,7 @@ pub fn approval_ppm_required() -> u32 {
|
||||
|
||||
/// The number of concrete validator approvals required for a proposal to pass.
|
||||
pub fn approvals_required() -> u32 {
|
||||
approval_ppm_required() * session::validator_count() as u32 / 1000
|
||||
approval_ppm_required() * session::validator_count() / 1000
|
||||
}
|
||||
|
||||
pub mod public {
|
||||
@@ -152,7 +152,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -178,7 +178,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -213,7 +213,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -239,7 +239,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -266,7 +266,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -294,7 +294,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
@@ -348,7 +348,7 @@ mod tests {
|
||||
with_externalities(&mut t, || {
|
||||
assert_eq!(staking::era_length(), 1u64);
|
||||
assert_eq!(staking::current_era(), 1u64);
|
||||
assert_eq!(session::validator_count(), 3usize);
|
||||
assert_eq!(session::validator_count(), 3u32);
|
||||
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
|
||||
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ pub mod timestamp;
|
||||
pub mod session;
|
||||
#[allow(unused)]
|
||||
pub mod governance;
|
||||
#[allow(unused)]
|
||||
pub mod parachains;
|
||||
|
||||
// TODO: polkadao
|
||||
// TODO: parachains
|
||||
|
||||
|
||||
#[cfg(feature = "with-std")]
|
||||
pub mod genesismap;
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// 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 runtime_std::mem;
|
||||
use codec::{Slicable, Joiner};
|
||||
use support::{Hashable, with_env, storage};
|
||||
use runtime::session;
|
||||
|
||||
const PARACHAIN_COUNT: &[u8] = b"par:cou";
|
||||
|
||||
/// Identifier for a chain, either one of a number of parachains or the relay chain.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub enum Chain {
|
||||
/// The relay chain.
|
||||
Relay,
|
||||
/// A parachain of the given index.
|
||||
Parachain(u32),
|
||||
}
|
||||
|
||||
/// The duty roster specifying what jobs each validator must do.
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[cfg_attr(test, derive(Default, Debug))]
|
||||
pub struct DutyRoster {
|
||||
/// Lookup from validator index to chain on which that validator has a duty to validate.
|
||||
pub validator_duty: Vec<Chain>,
|
||||
/// Lookup from validator index to chain on which that validator has a duty to guarantee
|
||||
/// availability.
|
||||
pub guarantor_duty: Vec<Chain>,
|
||||
}
|
||||
|
||||
/// Get the number of parachains registered at present.
|
||||
pub fn parachain_count() -> u32 {
|
||||
storage::get_or(PARACHAIN_COUNT, 0)
|
||||
}
|
||||
|
||||
/// Calculate the current block's duty roster.
|
||||
pub fn calculate_duty_roster() -> DutyRoster {
|
||||
let parachain_count = parachain_count();
|
||||
let validator_count = session::validator_count() as u32;
|
||||
let validators_per_parachain = (validator_count - 1) / parachain_count;
|
||||
let validators_on_relay = validator_count - validators_per_parachain * parachain_count;
|
||||
|
||||
let mut roles_val = (0..validator_count).map(|i| match i / validators_per_parachain {
|
||||
i if i < parachain_count => Chain::Parachain(i as u32),
|
||||
_ => Chain::Relay,
|
||||
}).collect::<Vec<_>>();
|
||||
let mut roles_gua = roles_val.clone();
|
||||
|
||||
let h = with_env(|e| e.parent_hash.clone());
|
||||
let mut seed = vec![].join(&h).join(b"validator_role_pairs").blake2_256();
|
||||
|
||||
// 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::from_slice(&seed[offset..offset + 4]).expect("using 4 bytes for a 32-byte quantity") as usize % remaining;
|
||||
let gua_index = u32::from_slice(&seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-byte quantity") as usize % remaining;
|
||||
|
||||
if offset == 24 {
|
||||
// into the last 8 bytes - rehash to gather new entropy
|
||||
seed = seed.blake2_256();
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_std::{with_externalities, twox_128, TestExternalities};
|
||||
use codec::{KeyedVec, Joiner};
|
||||
use support::{one, two, with_env};
|
||||
use primitives::AccountID;
|
||||
use runtime::{consensus, session};
|
||||
|
||||
fn simple_setup() -> TestExternalities {
|
||||
TestExternalities { storage: map![
|
||||
twox_128(b"ses:val:len").to_vec() => vec![].join(&8u32),
|
||||
twox_128(b"par:cou").to_vec() => vec![].join(&2u32)
|
||||
], }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_work() {
|
||||
let mut t = simple_setup();
|
||||
with_externalities(&mut t, || {
|
||||
with_env(|e| e.parent_hash = [0u8; 32]);
|
||||
let duty_roster = calculate_duty_roster();
|
||||
|
||||
assert_eq!(duty_roster.validator_duty.len(), 8);
|
||||
assert_eq!(duty_roster.guarantor_duty.len(), 8);
|
||||
for i in 0..2 {
|
||||
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);
|
||||
assert_eq!(duty_roster, DutyRoster {
|
||||
validator_duty: vec![Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Relay, Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0)],
|
||||
guarantor_duty: vec![Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Parachain(1), Chain::Relay],
|
||||
});
|
||||
|
||||
with_env(|e| e.parent_hash = [1u8; 32]);
|
||||
let duty_roster = calculate_duty_roster();
|
||||
|
||||
assert_eq!(duty_roster.validator_duty.len(), 8);
|
||||
assert_eq!(duty_roster.guarantor_duty.len(), 8);
|
||||
for i in 0..2 {
|
||||
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);
|
||||
assert_eq!(duty_roster, DutyRoster {
|
||||
validator_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(1), Chain::Relay, Chain::Parachain(0), Chain::Relay],
|
||||
guarantor_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Relay],
|
||||
});
|
||||
|
||||
with_env(|e| e.parent_hash = [2u8; 32]);
|
||||
let duty_roster = calculate_duty_roster();
|
||||
|
||||
assert_eq!(duty_roster.validator_duty.len(), 8);
|
||||
assert_eq!(duty_roster.guarantor_duty.len(), 8);
|
||||
for i in 0..2 {
|
||||
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);
|
||||
assert_eq!(duty_roster, DutyRoster {
|
||||
validator_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(0), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Parachain(1)],
|
||||
guarantor_duty: vec![Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(1), Chain::Parachain(1), Chain::Relay, Chain::Parachain(0), Chain::Parachain(0), Chain::Relay],
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,8 @@ pub fn length() -> BlockNumber {
|
||||
}
|
||||
|
||||
/// The number of validators currently.
|
||||
pub fn validator_count() -> usize {
|
||||
ValidatorStorageVec::count() as usize
|
||||
pub fn validator_count() -> u32 {
|
||||
ValidatorStorageVec::count() as u32
|
||||
}
|
||||
|
||||
/// The current era index.
|
||||
|
||||
@@ -60,41 +60,24 @@ pub mod internal {
|
||||
// populate environment from header.
|
||||
with_env(|e| {
|
||||
e.block_number = block.header.number;
|
||||
e.parent_hash = block.header.parent_hash;
|
||||
});
|
||||
|
||||
let ref header = block.header;
|
||||
|
||||
// check parent_hash is correct.
|
||||
assert!(
|
||||
header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.transactions.iter().map(Slicable::to_vec).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = enumerated_trie_root(&txs);
|
||||
debug_assert_hash(&header.transaction_root, &txs_root);
|
||||
assert!(header.transaction_root == txs_root, "Transaction trie root must be valid.");
|
||||
// any initial checks
|
||||
initial_checks(&block);
|
||||
|
||||
// execute transactions
|
||||
for tx in &block.transactions {
|
||||
super::execute_transaction(tx);
|
||||
}
|
||||
block.transactions.iter().for_each(super::execute_transaction);
|
||||
|
||||
// post-transactional book-keeping.
|
||||
staking::internal::check_new_era();
|
||||
session::internal::check_rotate_session();
|
||||
|
||||
// any final checks
|
||||
final_checks(&block);
|
||||
|
||||
// check storage root.
|
||||
let storage_root = storage_root();
|
||||
debug_assert_hash(&header.state_root, &storage_root);
|
||||
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
|
||||
|
||||
// any stuff that we do after taking the storage root.
|
||||
post_finalise(header);
|
||||
post_finalise(&block.header);
|
||||
}
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
@@ -103,6 +86,7 @@ pub mod internal {
|
||||
// populate environment from header.
|
||||
with_env(|e| {
|
||||
e.block_number = header.number;
|
||||
e.parent_hash = header.parent_hash;
|
||||
mem::swap(&mut header.digest, &mut e.digest);
|
||||
});
|
||||
|
||||
@@ -129,17 +113,6 @@ pub mod internal {
|
||||
|
||||
header
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-std")]
|
||||
fn debug_assert_hash(given: &Hash, expected: &Hash) {
|
||||
use support::HexDisplay;
|
||||
if given != expected {
|
||||
println!("Hash: given={}, expected={}", HexDisplay::from(given), HexDisplay::from(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-std"))]
|
||||
fn debug_assert_hash(_given: &Hash, _expected: &Hash) {}
|
||||
}
|
||||
|
||||
fn execute_transaction(utx: &UncheckedTransaction) {
|
||||
@@ -160,10 +133,35 @@ fn execute_transaction(utx: &UncheckedTransaction) {
|
||||
tx.function.dispatch(&tx.signed, &tx.input_data);
|
||||
}
|
||||
|
||||
fn final_checks(_block: &Block) {
|
||||
fn initial_checks(block: &Block) {
|
||||
let ref header = block.header;
|
||||
|
||||
// check parent_hash is correct.
|
||||
assert!(
|
||||
header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
|
||||
"Parent hash should be valid."
|
||||
);
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.transactions.iter().map(Slicable::to_vec).collect::<Vec<_>>();
|
||||
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
|
||||
let txs_root = enumerated_trie_root(&txs);
|
||||
debug_assert_hash(&header.transaction_root, &txs_root);
|
||||
assert!(header.transaction_root == txs_root, "Transaction trie root must be valid.");
|
||||
}
|
||||
|
||||
fn final_checks(block: &Block) {
|
||||
let ref header = block.header;
|
||||
|
||||
// check digest
|
||||
with_env(|e| {
|
||||
assert!(_block.header.digest == e.digest);
|
||||
assert!(header.digest == e.digest);
|
||||
});
|
||||
|
||||
// check storage root.
|
||||
let storage_root = storage_root();
|
||||
debug_assert_hash(&header.state_root, &storage_root);
|
||||
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
|
||||
}
|
||||
|
||||
fn post_finalise(header: &Header) {
|
||||
@@ -172,6 +170,17 @@ fn post_finalise(header: &Header) {
|
||||
storage::put(&header.number.to_keyed_vec(BLOCK_HASH_AT), &header.blake2_256());
|
||||
}
|
||||
|
||||
#[cfg(feature = "with-std")]
|
||||
fn debug_assert_hash(given: &Hash, expected: &Hash) {
|
||||
use support::HexDisplay;
|
||||
if given != expected {
|
||||
println!("Hash: given={}, expected={}", HexDisplay::from(given), HexDisplay::from(expected));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "with-std"))]
|
||||
fn debug_assert_hash(_given: &Hash, _expected: &Hash) {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -21,13 +21,15 @@ use runtime_std::mem;
|
||||
use runtime_std::cell::RefCell;
|
||||
use runtime_std::rc::Rc;
|
||||
|
||||
use primitives::{BlockNumber, Digest};
|
||||
use primitives::{BlockNumber, Digest, Hash};
|
||||
|
||||
#[derive(Default)]
|
||||
/// The information that can be accessed globally.
|
||||
pub struct Environment {
|
||||
/// The current block number.
|
||||
pub block_number: BlockNumber,
|
||||
/// The current block's parent hash.
|
||||
pub parent_hash: Hash,
|
||||
/// The current block digest.
|
||||
pub digest: Digest,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user