From 0ea5dc61bc537ab88c3179fea8d6d099c859670f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 10 Apr 2018 16:32:39 +0200 Subject: [PATCH] give polkadot control over round proposer based on random seed --- polkadot/api/src/lib.rs | 12 ++++++++---- polkadot/consensus/src/lib.rs | 13 +++++++++++++ substrate/bft/src/lib.rs | 26 +++++++++++--------------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index e178f1d547c24..bf747624c3945 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -31,9 +31,6 @@ extern crate substrate_state_machine as state_machine; #[macro_use] extern crate error_chain; -#[macro_use] -extern crate log; - #[cfg(test)] extern crate substrate_keyring as keyring; @@ -42,7 +39,7 @@ use client::Client; use polkadot_executor::Executor as LocalDispatch; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use state_machine::OverlayedChanges; -use primitives::{AccountId, BlockId, Index, SessionKey, Timestamp}; +use primitives::{AccountId, BlockId, Hash, Index, SessionKey, Timestamp}; use primitives::parachain::DutyRoster; use runtime::{Block, Header, UncheckedExtrinsic, Extrinsic, Call, TimestampCall}; @@ -126,6 +123,9 @@ pub trait PolkadotApi { /// Get validators at a given block. fn validators(&self, at: &Self::CheckedBlockId) -> Result>; + /// Get the value of the randomness beacon at a given block. + fn random_seed(&self, at: &Self::CheckedBlockId) -> Result; + /// Get the authority duty roster at a block. fn duty_roster(&self, at: &Self::CheckedBlockId) -> Result; @@ -191,6 +191,10 @@ impl PolkadotApi for Client> with_runtime!(self, at, ::runtime::Session::validators) } + fn random_seed(&self, at: &CheckedId) -> Result { + with_runtime!(self, at, ::runtime::System::random_seed) + } + fn duty_roster(&self, at: &CheckedId) -> Result { // duty roster can only be queried at the start of a block, // so we create a dummy. diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 00537960390a5..2630bea720b3e 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -495,6 +495,7 @@ impl bft::ProposerFactory for ProposerFactory let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?; let duty_roster = self.client.duty_roster(&checked_id)?; + let random_seed = self.client.random_seed(&checked_id)?; let group_info = make_group_info(duty_roster, authorities)?; let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash)); @@ -510,6 +511,7 @@ impl bft::ProposerFactory for ProposerFactory parent_hash, parent_number: parent_header.number, parent_id: checked_id, + random_seed, local_key: sign_with, client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), @@ -533,6 +535,7 @@ pub struct Proposer { parent_hash: HeaderHash, parent_number: BlockNumber, parent_id: C::CheckedBlockId, + random_seed: Hash, client: Arc, local_key: Arc, transaction_pool: Arc>, @@ -625,6 +628,16 @@ impl bft::Proposer for Proposer { Box::new(self.delay.clone().map_err(Error::from).and_then(move |_| evaluated)) } + fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { + use primitives::uint::U256; + + let len: U256 = authorities.len().into(); + let offset = U256::from_big_endian(&self.random_seed.0) % len; + let offset = offset.low_u64() as usize + round_number; + + authorities[offset % authorities.len()].clone() + } + fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { use bft::generic::Misbehavior as GenericMisbehavior; use primitives::bft::{MisbehaviorKind, MisbehaviorReport}; diff --git a/substrate/bft/src/lib.rs b/substrate/bft/src/lib.rs index e21051b1aef36..a0ffaf3968ad1 100644 --- a/substrate/bft/src/lib.rs +++ b/substrate/bft/src/lib.rs @@ -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 generic::Context for BftInstance

{ } 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)