Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
482a074
reshuffle consensus libraries
rphmeier Feb 8, 2018
917b092
polkadot-useful type definitions for statement table
rphmeier Feb 8, 2018
8e2fd3c
begin BftService
rphmeier Feb 10, 2018
776cf13
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 10, 2018
6abfed4
primary selection logic
rphmeier Feb 12, 2018
fc18524
bft service implementation without I/O
rphmeier Feb 12, 2018
017fd51
extract out `BlockImport` trait
rphmeier Feb 12, 2018
25990ee
Merge branch 'master' into rh-split-bft-table
rphmeier Feb 12, 2018
c33c3ff
allow bft primitives to compile on wasm
rphmeier Feb 12, 2018
acab9a3
Block builder (substrate)
gavofyork Feb 12, 2018
1830fa7
take polkadot-consensus down to the core.
rphmeier Feb 12, 2018
767a9d9
test for preemption
rphmeier Feb 12, 2018
7fc4b4d
fix test build
rphmeier Feb 12, 2018
9acd3f9
Fix wasm build
gavofyork Feb 12, 2018
ca5900f
Bulid on any block
gavofyork Feb 13, 2018
d11cfe1
Test for block builder.
gavofyork Feb 13, 2018
b973ccc
Block import tests for client.
gavofyork Feb 13, 2018
ec61865
Tidy ups
gavofyork Feb 13, 2018
23638cd
clean up block builder instantiation
rphmeier Feb 15, 2018
dda6d24
Merge branch 'rh-split-bft-table' into rh-justification-verification
rphmeier Feb 15, 2018
340ce39
justification verification logic
rphmeier Feb 15, 2018
170b0d1
JustifiedHeader and import
rphmeier Feb 15, 2018
6a1a851
Propert block generation for tests
arkpar Feb 15, 2018
1352765
network and tablerouter trait
rphmeier Feb 15, 2018
2758503
use statement import to drive creation of further statements
rphmeier Feb 15, 2018
a1247bd
Fixed rpc tests
arkpar Feb 15, 2018
a1a19b6
custom error type for consensus
rphmeier Feb 15, 2018
40a9496
create proposer
rphmeier Feb 15, 2018
9e4f273
asynchronous proposal evaluation
rphmeier Feb 15, 2018
673fc2c
Merge branch 'master' into rh-justification-verification
rphmeier Feb 15, 2018
8636b77
Merge branch 'rh-justification-verification' into rh-polkadot-propose
rphmeier Feb 15, 2018
a5c09c8
inherent transactions in polkadot runtime
rphmeier Feb 16, 2018
7b1a563
fix tests to match real polkadot block constraints
rphmeier Feb 16, 2018
8d08573
implicitly generate inherent functions
rphmeier Feb 16, 2018
2abbe6c
add inherent transaction functionality to block body
rphmeier Feb 20, 2018
5bace3a
Merge branch 'master' into rh-polkadot-propose
rphmeier Feb 20, 2018
a87afa7
block builder logic for polkadot
rphmeier Feb 20, 2018
e891649
some tests for the polkadot API
rphmeier Feb 20, 2018
5b3556c
avoid redundancy in native code compatibility check
rphmeier Feb 21, 2018
ad8a576
helper for extracting nonce
rphmeier Feb 21, 2018
40b5e4c
transaction pool implementation
rphmeier Feb 21, 2018
760aeff
transaction pool
rphmeier Feb 23, 2018
198ff7b
integrate transaction pool with proposer
rphmeier Feb 23, 2018
3d052fb
Merge branch 'master' into rh-transaction-pool
rphmeier Feb 25, 2018
fd8e624
indentation
rphmeier Feb 25, 2018
8458389
kill storage keys module
rphmeier Feb 25, 2018
81ff2ad
accept new transactions to replace old
rphmeier Mar 1, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
begin BftService
  • Loading branch information
rphmeier committed Feb 10, 2018
commit 8e2fd3cadef780ddce0a77f3015f6ab3d4c311bb
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 0 additions & 91 deletions polkadot/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,81 +371,6 @@ impl<C: Context> SharedTable<C> {
}
}

/// Errors that can occur during agreement.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error {
IoTerminated,
FaultyTimer,
CannotPropose,
}

impl From<bft::InputStreamConcluded> for Error {
fn from(_: bft::InputStreamConcluded) -> Error {
Error::IoTerminated
}
}

/// Context owned by the BFT future necessary to execute the logic.
pub struct BftContext<C: Context> {
context: C,
table: SharedTable<C>,
timer: Timer,
round_timeout_multiplier: u64,
}

impl<C: Context> bft::Context for BftContext<C>
where C::Proposal: 'static,
{
type AuthorityId = C::AuthorityId;
type Digest = C::Digest;
type Signature = C::Signature;
type Candidate = C::Proposal;
type RoundTimeout = Box<Future<Item=(),Error=Error>>;
type CreateProposal = Box<Future<Item=Self::Candidate,Error=Error>>;

fn local_id(&self) -> Self::AuthorityId {
self.context.local_id()
}

fn proposal(&self) -> Self::CreateProposal {
Box::new(self.table.get_proposal().map_err(|_| Error::CannotPropose))
}

fn candidate_digest(&self, candidate: &Self::Candidate) -> Self::Digest {
C::proposal_digest(candidate)
}

fn sign_local(&self, message: bft::Message<Self::Candidate, Self::Digest>)
-> bft::LocalizedMessage<Self::Candidate, Self::Digest, Self::AuthorityId, Self::Signature>
{
let sender = self.local_id();
let signature = self.context.sign_bft_message(&message);
bft::LocalizedMessage {
message,
sender,
signature,
}
}

fn round_proposer(&self, round: usize) -> Self::AuthorityId {
self.context.round_proposer(round)
}

fn candidate_valid(&self, proposal: &Self::Candidate) -> bool {
self.table.proposal_valid(proposal)
}

fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout {
let round = ::std::cmp::min(63, round) as u32;
let timeout = 1u64.checked_shl(round)
.unwrap_or_else(u64::max_value)
.saturating_mul(self.round_timeout_multiplier);

Box::new(self.timer.sleep(Duration::from_secs(timeout))
.map_err(|_| Error::FaultyTimer))
}
}


/// Parameters necessary for agreement.
pub struct AgreementParams<C: Context> {
Expand Down Expand Up @@ -477,22 +402,6 @@ pub trait MessageRecovery<C: Context> {
fn check_message(&self, Self::UncheckedMessage) -> Option<CheckedMessage<C>>;
}

/// A batch of statements to send out.
pub trait StatementBatch<V, T> {
/// Get the target authorities of these statements.
fn targets(&self) -> &[V];

/// If the batch is empty.
fn is_empty(&self) -> bool;

/// Push a statement onto the batch. Returns false when the batch is full.
///
/// This is meant to do work like incrementally serializing the statements
/// into a vector of bytes while making sure the length is below a certain
/// amount.
fn push(&mut self, statement: T) -> bool;
}

/// Recovered and fully checked messages.
pub enum CheckedMessage<C: Context> {
/// Messages meant for the BFT agreement logic.
Expand Down
2 changes: 2 additions & 0 deletions substrate/bft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ authors = ["Parity Technologies <[email protected]>"]

[dependencies]
futures = "0.1.17"
substrate-client = { path = "../client" }
substrate-primitives = { path = "../primitives" }
ed25519 = { path = "../ed25519" }
tokio-timer = "0.1.2"
144 changes: 73 additions & 71 deletions substrate/bft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ pub mod generic;

#[cfg_attr(test, macro_use)]
extern crate futures;
extern crate substrate_client as client;
extern crate substrate_primitives as primitives;
extern crate ed25519;
extern crate tokio_timer;

use client::Client;
use ed25519::Signature;
use primitives::block::HeaderHash;
use primitives::{Block, AuthorityId};
use primitives::block::{Block, Header, HeaderHash};
use primitives::AuthorityId;

use futures::{Stream, Sink, Future};
use tokio_timer::Timer;

pub use generic::InputStreamConcluded;

Expand Down Expand Up @@ -56,96 +60,94 @@ pub type Committed = generic::Committed<Block, HeaderHash, Signature>;
/// Communication between BFT participants.
pub type Communication = generic::Communication<Block, HeaderHash, AuthorityId, Signature>;

/// Context necessary for agreement.
pub trait Context {
/// A future that resolves when a round timeout is concluded.
type RoundTimeout: Future<Item=()>;
/// A future that resolves when a proposal is ready.
type CreateProposal: Future<Item=Block>;

/// Get the local authority ID.
fn local_id(&self) -> AuthorityId;

/// Get the best proposal.
fn proposal(&self) -> Self::CreateProposal;

/// Get the digest of a candidate.
fn candidate_digest(&self, candidate: &Block) -> HeaderHash;

/// Sign a message using the local authority ID.
fn sign_local(&self, message: Message) -> LocalizedMessage;
/// Errors that can occur during agreement.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Error {
/// Io streams terminated.
IoTerminated,
/// Timer failed to fire.
FaultyTimer,
/// Unable to propose for some reason.
CannotPropose,
}

/// Get the proposer for a given round of consensus.
fn round_proposer(&self, round: usize) -> AuthorityId;
impl From<InputStreamConcluded> for Error {
fn from(_: InputStreamConcluded) -> Error {
Error::IoTerminated;
}
}

/// Whether the candidate is valid.
fn candidate_valid(&self, candidate: &Block) -> bool;
/// Logic for a proposer.
///
/// This will encapsulate creation and evaluation of proposals at a specific
/// block.
pub trait Proposer: Sized {
type CreateProposal: IntoFuture<Item=Block,Error=Error>;

/// Initialize the proposal logic on top of a specific header.
// TODO: provide state context explicitly?
fn init(parent_header: &Header, sign_with: ed25519::Pair) -> Self;

/// Create a proposal.
fn propose(&self) -> Self::CreateProposal;
/// Evaluate proposal. True means valid.
// TODO: change this to a future.
fn evaluate(&self, proposal: &Block) -> bool;
}

/// Create a round timeout. The context will determine the correct timeout
/// length, and create a future that will resolve when the timeout is
/// concluded.
fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout;
/// Instance of BFT agreement.
pub struct BftInstance<P> {
key: ed25519::Pair, // TODO (now): key changing over time.
authorities: Vec<AuthorityId>,
parent_hash: HeaderHash,
timer: Timer,
round_timeout_multiplier: u64,
proposer: P,
}

impl<T: Context> generic::Context for T {
type Candidate = Block;
type Digest = HeaderHash;
impl<P: Proposer> generic::Context for BftInstance<P> {
type AuthorityId = AuthorityId;
type Digest = HeaderHash;
type Signature = Signature;
type RoundTimeout = <Self as Context>::RoundTimeout;
type CreateProposal = <Self as Context>::CreateProposal;
type Candidate = Block;
type RoundTimeout = Box<Future<Item=(),Error=Error>>;
type CreateProposal = P::CreateProposal;

fn local_id(&self) -> AuthorityId { Context::local_id(self) }
fn local_id(&self) -> AuthorityId {
self.key.public()
}

fn proposal(&self) -> Self::CreateProposal { Context::proposal(self) }
fn proposal(&self) -> P::CreateProposal {
self.proposer.propose()
}

fn candidate_digest(&self, candidate: &Block) -> HeaderHash {
Context::candidate_digest(self, candidate)
fn candidate_digest(&self, proposal: &Block) -> HeaderHash {
unimplemented!() // TODO: calculate header hash.
}

fn sign_local(&self, message: Message) -> LocalizedMessage {
Context::sign_local(self, message)
unimplemented!() // TODO: figure out message encoding.
}

fn round_proposer(&self, round: usize) -> AuthorityId {
Context::round_proposer(self, round)
// repeat blake2_256 on parent hash round + 1 times.
// use as index into authorities vec.
unimplemented!()
}

fn candidate_valid(&self, candidate: &Block) -> bool {
Context::candidate_valid(self, candidate)
fn candidate_valid(&self, proposal: &Block) -> bool {
self.proposer.evaluate(proposal)
}

fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout {
Context::begin_round_timeout(self, round)
let round = ::std::cmp::min(63, round) as u32;
let timeout = 1u64.checked_shl(round)
.unwrap_or_else(u64::max_value)
.saturating_mul(self.round_timeout_multiplier);

Box::new(self.timer.sleep(Duration::from_secs(timeout))
.map_err(|_| Error::FaultyTimer))
}
}

/// Attempt to reach BFT agreement on a candidate.
///
/// `nodes` is the number of nodes in the system.
/// `max_faulty` is the maximum number of faulty nodes. Should be less than
/// 1/3 of `nodes`, otherwise agreement may never be reached.
///
/// The input stream should never logically conclude. The logic here assumes
/// that messages flushed to the output stream will eventually reach other nodes.
///
/// Note that it is possible to witness agreement being reached without ever
/// seeing the candidate. Any candidates seen will be checked for validity.
///
/// Although technically the agreement will always complete (given the eventual
/// delivery of messages), in practice it is possible for this future to
/// conclude without having witnessed the conclusion.
/// In general, this future should be pre-empted by the import of a justification
/// set for this block height.
pub fn agree<C: Context, I, O, E>(context: C, nodes: usize, max_faulty: usize, input: I, output: O)
-> generic::Agreement<C, I, O>
where
C: Context,
C::RoundTimeout: Future<Error=E>,
C::CreateProposal: Future<Error=E>,
I: Stream<Item=Communication,Error=E>,
O: Sink<SinkItem=Communication,SinkError=E>,
E: From<InputStreamConcluded>,
{
generic::agree(context, nodes, max_faulty, input, output)
}
/// Bft service built around
54 changes: 54 additions & 0 deletions substrate/primitives/src/bft.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.

// Substrate 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.

// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Message formats for the BFT consensus layer.

use block::{Block, HeaderHash};
use codec::{Slicable, Input};

#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(u8)]
enum ActionKind {
Propose = 1,
Prepare = 2,
Commit = 3,
AdvanceRound = 4,
}

/// Actions which can be taken during the BFT process.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Action {
/// Proposal of a block candidate.
Propose(usize, Block),
/// Preparation to commit for a candidate.
Prepare(usize, HeaderHash),
/// Vote to commit to a candidate.
Commit(usize, HeaderHash),
/// Vote to advance round after inactive primary.
AdvanceRound(usize),
}

/// Messages exchanged between participants in the BFT consensus.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct Message {
/// The parent header hash this action is relative to.
pub parent: HeaderHash,
/// The action being broadcasted.
pub action: Action,
}
5 changes: 5 additions & 0 deletions substrate/primitives/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ impl Header {
digest: Default::default(),
}
}

/// Get the blake2-256 hash of this header.
pub fn hash(&self) -> HeaderHash {
hashing::blake2_256(Slicable::to_vec(self).as_slice())
}
}

impl Slicable for Header {
Expand Down