From 702bf9595f0366e37029eccf93a8620cdcdb21b9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 12 Apr 2018 13:59:17 +0200 Subject: [PATCH] delay before voting on proposals --- polkadot/api/src/lib.rs | 2 +- polkadot/consensus/src/error.rs | 5 +++++ polkadot/consensus/src/lib.rs | 29 ++++++++++++++++++++++++----- polkadot/consensus/src/service.rs | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index 98e439875aa2f..0728a5a1f7718 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -100,7 +100,7 @@ pub trait BlockBuilder: Sized { } /// A checked block identifier. -pub trait CheckedBlockId: Clone { +pub trait CheckedBlockId: Clone + 'static { /// Yield the underlying block ID. fn block_id(&self) -> &BlockId; } diff --git a/polkadot/consensus/src/error.rs b/polkadot/consensus/src/error.rs index 38ba4ab60716f..127e449b4a7a1 100644 --- a/polkadot/consensus/src/error.rs +++ b/polkadot/consensus/src/error.rs @@ -24,6 +24,11 @@ error_chain! { Bft(::bft::Error, ::bft::ErrorKind); } + foreign_links { + Io(::std::io::Error); + SharedIo(::futures::future::SharedError<::std::io::Error>); + } + errors { InvalidDutyRosterLength(expected: usize, got: usize) { description("Duty Roster had invalid length"), diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 53cf39b6c8f23..8a2ed55ecc1f8 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -70,7 +70,9 @@ use transaction_pool::{Ready, TransactionPool, PolkadotBlock}; use futures::prelude::*; use futures::future; +use future::Shared; use parking_lot::Mutex; +use tokio_core::reactor::{Handle, Timeout}; pub use self::error::{ErrorKind, Error}; pub use service::Service; @@ -475,6 +477,8 @@ pub struct ProposerFactory { pub transaction_pool: Arc>, /// The backing network handle. pub network: N, + /// Handle to the underlying tokio-core. + pub handle: Handle, } impl bft::ProposerFactory for ProposerFactory { @@ -482,6 +486,10 @@ impl bft::ProposerFactory for ProposerFactory type Error = Error; fn init(&self, parent_header: &SubstrateHeader, authorities: &[AuthorityId], sign_with: Arc) -> Result { + use std::time::Duration; + + const DELAY_UNTIL: Duration = Duration::from_millis(5000); + let parent_hash = parent_header.blake2_256().into(); let checked_id = self.client.check_id(BlockId::Hash(parent_hash))?; @@ -491,6 +499,11 @@ impl bft::ProposerFactory for ProposerFactory let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash)); let router = self.network.table_router(table.clone()); + let timeout = Timeout::new(DELAY_UNTIL, &self.handle)?; + + debug!(target: "bft", "Initialising consensus proposer. Refusing to evaluate for {:?} from now.", + DELAY_UNTIL); + // TODO [PoC-2]: kick off collation process. Ok(Proposer { parent_hash, @@ -499,6 +512,7 @@ impl bft::ProposerFactory for ProposerFactory local_key: sign_with, client: self.client.clone(), transaction_pool: self.transaction_pool.clone(), + delay: timeout.shared(), _table: table, _router: router, }) @@ -521,6 +535,7 @@ pub struct Proposer { client: Arc, local_key: Arc, transaction_pool: Arc>, + delay: Shared, _table: Arc, _router: R, } @@ -528,9 +543,9 @@ pub struct Proposer { impl bft::Proposer for Proposer { type Error = Error; type Create = Result; - type Evaluate = Result; + type Evaluate = Box>; - fn propose(&self) -> Result { + fn propose(&self) -> Self::Create { debug!(target: "bft", "proposing block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); // TODO: handle case when current timestamp behind that in state. @@ -581,9 +596,10 @@ impl bft::Proposer for Proposer { } // TODO: certain kinds of errors here should lead to a misbehavior report. - fn evaluate(&self, proposal: &SubstrateBlock) -> Result { + fn evaluate(&self, proposal: &SubstrateBlock) -> Self::Evaluate { debug!(target: "bft", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); - match evaluate_proposal(proposal, &*self.client, current_timestamp(), &self.parent_hash, &self.parent_id) { + + let evaluated = match evaluate_proposal(proposal, &*self.client, current_timestamp(), &self.parent_hash, &self.parent_id) { Ok(x) => Ok(x), Err(e) => match *e.kind() { ErrorKind::PolkadotApi(polkadot_api::ErrorKind::Executor(_)) => Ok(false), @@ -593,7 +609,10 @@ impl bft::Proposer for Proposer { ErrorKind::ProposalTooLarge(_) => Ok(false), _ => Err(e), } - } + }; + + // delay casting vote until able. + Box::new(self.delay.clone().map_err(Error::from).and_then(move |_| evaluated)) } fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior)>) { diff --git a/polkadot/consensus/src/service.rs b/polkadot/consensus/src/service.rs index 3671edfb90f3e..66a6b3d8a75af 100644 --- a/polkadot/consensus/src/service.rs +++ b/polkadot/consensus/src/service.rs @@ -275,6 +275,7 @@ impl Service { client: client.clone(), transaction_pool: transaction_pool.clone(), network: Network(network.clone()), + handle: core.handle(), }; let messages = SharedMessageCollection::new(); let bft_service = Arc::new(BftService::new(client.clone(), key, factory));