Fixes and improvements for PoC-1 Testnet (#143)

* Fix initialisations and add a test.

* Fix test.

* Fix overflow bug.

* Minor refactoring and fixes.

* Fix vote threshold.

* Add note.

* Fixes for latest rust and the readme.

* Better readme.

* An extra validator for PoC-1

* Update README.

* PoC-1 bootnodes.

* don't return async::notready for messages without scheduling wakeup

* Fix endowed account

* give polkadot control over round proposer based on random seed

* address grumbles.
This commit is contained in:
Gav Wood
2018-05-07 15:25:47 +02:00
committed by GitHub
parent 2232da2d53
commit 5d9481140c
27 changed files with 307 additions and 59 deletions
+11 -15
View File
@@ -133,11 +133,16 @@ pub trait Proposer {
/// Create a proposal.
fn propose(&self) -> Self::Create;
/// Evaluate proposal. True means valid.
// TODO: change this to a future.
fn evaluate(&self, proposal: &Block) -> Self::Evaluate;
/// Import witnessed misbehavior.
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>);
/// Determine the proposer for a given round. This should be a deterministic function
/// with consistent results across all authorities.
fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId;
}
/// Block import trait.
@@ -189,20 +194,7 @@ impl<P: Proposer> generic::Context for BftInstance<P> {
}
fn round_proposer(&self, round: usize) -> AuthorityId {
use primitives::hashing::blake2_256;
// repeat blake2_256 on parent hash round + 1 times.
// use as index into authorities vec.
// TODO: parent hash is really insecure as a randomness beacon as
// the prior can easily influence the block hash.
let hashed = (0..round + 1).fold(self.parent_hash.0, |a, _| {
blake2_256(&a[..])
});
let index = u32::decode(&mut &hashed[..])
.expect("there are more than 4 bytes in a 32 byte hash; qed");
self.authorities[(index as usize) % self.authorities.len()]
self.proposer.round_proposer(round, &self.authorities[..])
}
fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal {
@@ -649,6 +641,10 @@ mod tests {
}
fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {}
fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId {
authorities[round_number % authorities.len()].clone()
}
}
fn make_service(client: FakeClient)
+38 -6
View File
@@ -23,8 +23,9 @@ mod tests;
use std::sync::Arc;
use client::{self, Client};
use primitives::block;
use primitives::{block, Hash, blake2_256};
use primitives::storage::{StorageKey, StorageData};
use primitives::hexdisplay::HexDisplay;
use state_machine;
use self::error::Result;
@@ -40,6 +41,22 @@ build_rpc_trait! {
#[rpc(name = "state_callAt")]
fn call_at(&self, String, Vec<u8>, block::HeaderHash) -> Result<Vec<u8>>;
/// Returns the hash of a storage entry.
#[rpc(name = "state_getStorageHashAt")]
fn storage_hash_at(&self, StorageKey, block::HeaderHash) -> Result<Hash>;
/// Returns the size of a storage entry.
#[rpc(name = "state_getStorageSizeAt")]
fn storage_size_at(&self, StorageKey, block::HeaderHash) -> Result<u64>;
/// Returns the hash of a storage entry.
#[rpc(name = "state_getStorageHash")]
fn storage_hash(&self, StorageKey) -> Result<Hash>;
/// Returns the size of a storage entry.
#[rpc(name = "state_getStorageSize")]
fn storage_size(&self, StorageKey) -> Result<u64>;
/// Returns a storage entry.
#[rpc(name = "state_getStorage")]
fn storage(&self, StorageKey) -> Result<StorageData>;
@@ -56,21 +73,36 @@ impl<B, E> StateApi for Arc<Client<B, E>> where
client::error::Error: From<<<B as client::backend::Backend>::State as state_machine::backend::Backend>::Error>,
{
fn storage_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
trace!(target: "rpc", "Querying storage at {:?} for key {}", block, HexDisplay::from(&key.0));
Ok(self.as_ref().storage(&block::Id::Hash(block), &key)?)
}
fn call_at(&self, method: String, data: Vec<u8>, block: block::HeaderHash) -> Result<Vec<u8>> {
trace!(target: "rpc", "Calling runtime at {:?} for method {} ({})", block, method, HexDisplay::from(&data));
Ok(self.as_ref().call(&block::Id::Hash(block), &method, &data)?.return_data)
}
fn storage_hash_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<Hash> {
self.storage_at(key, block).map(|x| blake2_256(&x.0).into())
}
fn storage_size_at(&self, key: StorageKey, block: block::HeaderHash) -> Result<u64> {
self.storage_at(key, block).map(|x| x.0.len() as u64)
}
fn storage_hash(&self, key: StorageKey) -> Result<Hash> {
self.storage_hash_at(key, self.as_ref().info()?.chain.best_hash)
}
fn storage_size(&self, key: StorageKey) -> Result<u64> {
self.storage_size_at(key, self.as_ref().info()?.chain.best_hash)
}
fn storage(&self, key: StorageKey) -> Result<StorageData> {
let at = block::Id::Hash(self.as_ref().info()?.chain.best_hash);
use primitives::hexdisplay::HexDisplay;
info!("Querying storage at {:?} for key {}", at, HexDisplay::from(&key.0));
Ok(self.as_ref().storage(&at, &key)?)
self.storage_at(key, self.as_ref().info()?.chain.best_hash)
}
fn call(&self, method: String, data: Vec<u8>) -> Result<Vec<u8>> {
Ok(self.as_ref().call(&block::Id::Hash(self.as_ref().info()?.chain.best_hash), &method, &data)?.return_data)
self.call_at(method, data, self.as_ref().info()?.chain.best_hash)
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
#![cfg_attr(not(feature = "std"), feature(lang_items))]
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#![cfg_attr(not(feature = "std"), feature(macro_reexport))]
#![cfg_attr(not(feature = "std"), feature(use_extern_macros))]
#![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")]
#![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")]
@@ -15,7 +15,6 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "nightly")]
#[macro_reexport(vec)]
extern crate alloc;
#[cfg(feature = "nightly")]
extern crate pwasm_libc;
@@ -49,6 +49,8 @@ impl<S: codec::Slicable + Default> StorageVec for AuthorityStorageVec<S> {
pub const CODE: &'static [u8] = b":code";
pub type KeyValue = (Vec<u8>, Vec<u8>);
pub trait Trait: system::Trait {
type PublicAux: RefInto<Self::AccountId>;
type SessionKey: Parameter + Default;
@@ -61,6 +63,7 @@ decl_module! {
}
pub enum PrivCall {
fn set_code(new: Vec<u8>) = 0;
fn set_storage(items: Vec<KeyValue>) = 1;
}
}
@@ -75,6 +78,13 @@ impl<T: Trait> Module<T> {
storage::unhashed::put_raw(CODE, &new);
}
/// Set some items of storage.
fn set_storage(items: Vec<KeyValue>) {
for i in &items {
storage::unhashed::put_raw(&i.0, &i.1);
}
}
/// Report some misbehaviour.
fn report_misbehavior(_aux: &T::PublicAux, _report: MisbehaviorReport) {
// TODO.
@@ -558,7 +558,8 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
twox_128(<ActiveCouncil<T>>::key()).to_vec() => self.active_council.encode(),
twox_128(<voting::CooloffPeriod<T>>::key()).to_vec() => self.cooloff_period.encode(),
twox_128(<voting::VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode()
twox_128(<voting::VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
twox_128(<voting::Proposals<T>>::key()).to_vec() => vec![0u8; 0].encode()
]
}
}
@@ -42,7 +42,7 @@ decl_storage! {
pub CooloffPeriod get(cooloff_period): b"cov:cooloff" => required T::BlockNumber;
pub VotingPeriod get(voting_period): b"cov:period" => required T::BlockNumber;
pub Proposals get(proposals): b"cov:prs" => default Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry.
pub Proposals get(proposals): b"cov:prs" => required Vec<(T::BlockNumber, T::Hash)>; // ordered by expiry.
pub ProposalOf get(proposal_of): b"cov:pro" => map [ T::Hash => T::Proposal ];
pub ProposalVoters get(proposal_voters): b"cov:voters:" => default map [ T::Hash => Vec<T::AccountId> ];
pub CouncilVoteOf get(vote_of): b"cov:vote:" => map [ (T::Hash, T::AccountId) => bool ];
@@ -79,6 +79,7 @@ impl<T: Trait> Module<T> {
let proposal_hash = T::Hashing::hash_of(&proposal);
assert!(!<ProposalOf<T>>::exists(proposal_hash), "No duplicate proposals allowed");
assert!(!Self::is_vetoed(&proposal_hash));
let mut proposals = Self::proposals();
@@ -85,9 +85,9 @@ decl_storage! {
pub VotingPeriod get(voting_period): b"dem:per" => required T::BlockNumber;
// The next free referendum index, aka the number of referendums started so far.
pub ReferendumCount get(referendum_count): b"dem:rco" => default ReferendumIndex;
pub ReferendumCount get(referendum_count): b"dem:rco" => required ReferendumIndex;
// The next referendum index that should be tallied.
pub NextTally get(next_tally): b"dem:nxt" => default ReferendumIndex;
pub NextTally get(next_tally): b"dem:nxt" => required ReferendumIndex;
// Information concerning any given referendum.
pub ReferendumInfoOf get(referendum_info): b"dem:pro:" => map [ ReferendumIndex => (T::BlockNumber, T::Proposal, VoteThreshold) ];
@@ -318,7 +318,10 @@ impl<T: Trait> primitives::BuildExternalities for GenesisConfig<T>
map![
twox_128(<LaunchPeriod<T>>::key()).to_vec() => self.launch_period.encode(),
twox_128(<VotingPeriod<T>>::key()).to_vec() => self.voting_period.encode(),
twox_128(<MinimumDeposit<T>>::key()).to_vec() => self.minimum_deposit.encode()
twox_128(<MinimumDeposit<T>>::key()).to_vec() => self.minimum_deposit.encode(),
twox_128(<ReferendumCount<T>>::key()).to_vec() => (0 as ReferendumIndex).encode(),
twox_128(<NextTally<T>>::key()).to_vec() => (0 as ReferendumIndex).encode(),
twox_128(<PublicPropCount<T>>::key()).to_vec() => (0 as PropIndex).encode()
]
}
}
@@ -16,9 +16,9 @@
//! Voting thresholds.
use primitives::traits::IntegerSquareRoot;
use primitives::traits::{Zero, IntegerSquareRoot};
use codec::{Input, Slicable};
use rstd::ops::{Add, Mul, Div};
use rstd::ops::{Add, Mul, Div, Rem};
/// A means of determining if a vote is past pass threshold.
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -58,18 +58,60 @@ pub trait Approved<Balance> {
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool;
}
impl<Balance: IntegerSquareRoot + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
/// Return `true` iff `n1 / d1 < n2 / d2`. `d1` and `d2` may not be zero.
fn compare_rationals<T: Zero + Mul<T, Output = T> + Div<T, Output = T> + Rem<T, Output = T> + Ord + Copy>(mut n1: T, mut d1: T, mut n2: T, mut d2: T) -> bool {
// Uses a continued fractional representation for a non-overflowing compare.
// Detailed at https://janmr.com/blog/2014/05/comparing-rational-numbers-without-overflow/.
loop {
let q1 = n1 / d1;
let q2 = n2 / d2;
if q1 < q2 {
return true;
}
if q2 < q1 {
return false;
}
let r1 = n1 % d1;
let r2 = n2 % d2;
if r2.is_zero() {
return false;
}
if r1.is_zero() {
return true;
}
n1 = d2;
n2 = d1;
d1 = r2;
d2 = r1;
}
}
impl<Balance: IntegerSquareRoot + Zero + Ord + Add<Balance, Output = Balance> + Mul<Balance, Output = Balance> + Div<Balance, Output = Balance> + Rem<Balance, Output = Balance> + Copy> Approved<Balance> for VoteThreshold {
/// Given `approve` votes for and `against` votes against from a total electorate size of
/// `electorate` (`electorate - (approve + against)` are abstainers), then returns true if the
/// overall outcome is in favour of approval.
fn approved(&self, approve: Balance, against: Balance, electorate: Balance) -> bool {
let voters = approve + against;
let sqrt_voters = voters.integer_sqrt();
let sqrt_electorate = electorate.integer_sqrt();
if sqrt_voters.is_zero() { return false; }
match *self {
VoteThreshold::SuperMajorityApprove =>
voters.integer_sqrt() * approve / electorate.integer_sqrt() > against,
compare_rationals(against, sqrt_voters, approve, sqrt_electorate),
VoteThreshold::SuperMajorityAgainst =>
approve > voters.integer_sqrt() * against / electorate.integer_sqrt(),
compare_rationals(against, sqrt_electorate, approve, sqrt_voters),
VoteThreshold::SimpleMajority => approve > against,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_work() {
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(60, 50, 210), false);
assert_eq!(VoteThreshold::SuperMajorityApprove.approved(100, 50, 210), true);
}
}