define context trait and initialize statement table

This commit is contained in:
Robert Habermeier
2018-01-03 17:06:04 +01:00
parent f2cbae5948
commit acca871d20
6 changed files with 235 additions and 25 deletions
+7 -7
View File
@@ -83,13 +83,13 @@ 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 Proposal: Future<Item=Self::Candidate>;
type CreateProposal: Future<Item=Self::Candidate>;
/// Get the local validator ID.
fn local_id(&self) -> Self::ValidatorId;
/// Get the best proposal.
fn proposal(&self) -> Self::Proposal;
fn proposal(&self) -> Self::CreateProposal;
/// Get the digest of a candidate.
fn candidate_digest(&self, candidate: &Self::Candidate) -> Self::Digest;
@@ -247,7 +247,7 @@ enum LocalState {
struct Strategy<C: Context> {
nodes: usize,
max_faulty: usize,
fetching_proposal: Option<C::Proposal>,
fetching_proposal: Option<C::CreateProposal>,
round_timeout: future::Fuse<C::RoundTimeout>,
local_state: LocalState,
locked: Option<Locked<C::Digest, C::Signature>>,
@@ -330,7 +330,7 @@ impl<C: Context> Strategy<C> {
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, E>
where
C::RoundTimeout: Future<Error=E>,
C::Proposal: Future<Error=E>,
C::CreateProposal: Future<Error=E>,
{
let mut last_watermark = (
self.current_accumulator.round_number(),
@@ -363,7 +363,7 @@ impl<C: Context> Strategy<C> {
-> Poll<Committed<C::Candidate, C::Digest, C::Signature>, E>
where
C::RoundTimeout: Future<Error=E>,
C::Proposal: Future<Error=E>,
C::CreateProposal: Future<Error=E>,
{
self.propose(context, sending)?;
self.prepare(context, sending);
@@ -413,7 +413,7 @@ impl<C: Context> Strategy<C> {
}
fn propose(&mut self, context: &C, sending: &mut Sending<ContextCommunication<C>>)
-> Result<(), <C::Proposal as Future>::Error>
-> Result<(), <C::CreateProposal as Future>::Error>
{
if let LocalState::Start = self.local_state {
let mut propose = false;
@@ -629,7 +629,7 @@ impl<C, I, O, E> Future for Agreement<C, I, O>
where
C: Context,
C::RoundTimeout: Future<Error=E>,
C::Proposal: Future<Error=E>,
C::CreateProposal: Future<Error=E>,
I: Stream<Item=ContextCommunication<C>,Error=E>,
O: Sink<SinkItem=ContextCommunication<C>,SinkError=E>,
E: From<InputStreamConcluded>,
@@ -104,13 +104,13 @@ impl Context for TestContext {
type ValidatorId = ValidatorId;
type Signature = Signature;
type RoundTimeout = Box<Future<Item=(), Error=Error>>;
type Proposal = FutureResult<Candidate, Error>;
type CreateProposal = FutureResult<Candidate, Error>;
fn local_id(&self) -> ValidatorId {
self.local_id.clone()
}
fn proposal(&self) -> Self::Proposal {
fn proposal(&self) -> Self::CreateProposal {
let proposal = {
let mut p = self.proposal.lock().unwrap();
let x = *p;
+162 -1
View File
@@ -30,7 +30,168 @@
//! Groups themselves may be compromised by malicious validators.
extern crate futures;
extern crate polkadot_primitives as primitives;
extern crate parking_lot;
extern crate tokio_timer;
pub mod bft;
pub mod table;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use futures::prelude::*;
use tokio_timer::Timer;
use table::Table;
/// Context necessary for agreement.
pub trait Context: Send + Clone {
/// A validator ID
type ValidatorId: Debug + Hash + Eq + Clone;
/// The digest (hash or other unique attribute) of a candidate.
type Digest: Debug + Hash + Eq + Clone;
/// The group ID type
type GroupId: Debug + Hash + Ord + Eq + Clone;
/// A signature type.
type Signature: Debug + Eq + Clone;
/// Candidate type. In practice this will be a candidate receipt.
type ParachainCandidate: Debug + Ord + Eq + Clone;
/// The actual block proposal type. This is what is agreed upon, and
/// is composed of multiple candidates.
type Proposal: Debug + Eq + Clone;
/// A future that resolves when a candidate is checked for validity.
///
/// In Polkadot, this will involve fetching the corresponding block data,
/// producing the necessary ingress, and running the parachain validity function.
type CheckCandidate: IntoFuture<Item=bool>;
/// A future that resolves when availability of a candidate's external
/// data is checked.
type CheckAvailability: IntoFuture<Item=bool>;
/// Get the digest of a candidate.
fn candidate_digest(candidate: &Self::ParachainCandidate) -> Self::Digest;
/// Get the group of a candidate.
fn candidate_group(candidate: &Self::ParachainCandidate) -> Self::GroupId;
/// Get the primary for a given round.
fn round_proposer(&self, round: usize) -> Self::ValidatorId;
/// Check a candidate for validity.
fn check_validity(&self, candidate: &Self::ParachainCandidate) -> Self::CheckCandidate;
/// Check availability of candidate data.
fn check_availability(&self, candidate: &Self::ParachainCandidate) -> Self::CheckAvailability;
/// Attempt to combine a set of parachain candidates into a proposal.
///
/// This may arbitrarily return `None`, but the intent is for `Some`
/// to only be returned when candidates from enough groups are known.
///
/// "enough" may be subjective as well.
fn create_proposal(&self, candidates: Vec<&Self::ParachainCandidate>)
-> Option<Self::Proposal>;
/// Check validity of a proposal. This may also be somewhat subjective
/// based on a monotonic-decreasing curve.
fn proposal_valid(&self, proposal: &Self::Proposal) -> bool;
/// Get the local validator ID.
fn local_id(&self) -> Self::ValidatorId;
/// Sign a table validity statement with the local key.
fn sign_table_statement(
&self,
statement: &table::Statement<Self::ParachainCandidate, Self::Digest>
) -> Self::Signature;
/// Sign a BFT agreement message.
fn sign_bft_message(&self, &bft::Message<Self::Proposal, Self::Digest>) -> Self::Signature;
}
/// Information about a specific group.
#[derive(Debug, Clone)]
pub struct GroupInfo<V: Hash + Eq> {
/// Validators meant to check validity of candidates.
pub validity_guarantors: HashSet<V>,
/// Validators meant to check availability of candidate data.
pub availability_guarantors: HashSet<V>,
/// Number of votes needed for validity.
pub needed_validity: usize,
/// Number of votes needed for availability.
pub needed_availability: usize,
}
struct TableContext<C: Context> {
context: C,
groups: HashMap<C::GroupId, GroupInfo<C::ValidatorId>>,
}
impl<C: Context> table::Context for TableContext<C> {
type ValidatorId = C::ValidatorId;
type Digest = C::Digest;
type GroupId = C::GroupId;
type Signature = C::Signature;
type Candidate = C::ParachainCandidate;
fn candidate_digest(candidate: &Self::Candidate) -> Self::Digest {
C::candidate_digest(candidate)
}
fn candidate_group(candidate: &Self::Candidate) -> Self::GroupId {
C::candidate_group(candidate)
}
fn is_member_of(&self, validator: &Self::ValidatorId, group: &Self::GroupId) -> bool {
self.groups.get(group).map_or(false, |g| g.validity_guarantors.contains(validator))
}
fn is_availability_guarantor_of(&self, validator: &Self::ValidatorId, group: &Self::GroupId) -> bool {
self.groups.get(group).map_or(false, |g| g.availability_guarantors.contains(validator))
}
fn requisite_votes(&self, group: &Self::GroupId) -> (usize, usize) {
self.groups.get(group).map_or(
(usize::max_value(), usize::max_value()),
|g| (g.needed_validity, g.needed_availability),
)
}
}
/// Parameters necessary for agreement.
pub struct AgreementParams<C: Context> {
/// The context itself.
pub context: C,
/// For scheduling timeouts.
pub timer: Timer,
/// Group assignments.
pub groups: HashMap<C::GroupId, GroupInfo<C::ValidatorId>>,
/// The local candidate proposal.
// TODO: replace with future.
pub local_proposal: Option<C::ParachainCandidate>,
}
pub fn agree<C: Context + Clone>(params: AgreementParams<C>) {
let context = params.context;
let local_id = context.local_id();
let mut table = Table::<TableContext<C>>::default();
let table_context = TableContext {
context: context.clone(),
groups: params.groups,
};
if let Some(candidate) = params.local_proposal {
let statement = table::Statement::Candidate(candidate);
let signed_statement = table::SignedStatement {
signature: context.sign_table_statement(&statement),
sender: local_id.clone(),
statement: statement,
};
table.import_statement(&table_context, signed_statement, None);
}
}
+22 -13
View File
@@ -35,21 +35,21 @@ use std::fmt::Debug;
/// Context for the statement table.
pub trait Context {
/// A validator ID
type ValidatorId: Hash + Eq + Clone + Debug;
type ValidatorId: Debug + Hash + Eq + Clone;
/// The digest (hash or other unique attribute) of a candidate.
type Digest: Hash + Eq + Clone + Debug;
/// Candidate type.
type Candidate: Ord + Eq + Clone + Debug;
type Digest: Debug + Hash + Eq + Clone;
/// The group ID type
type GroupId: Hash + Ord + Eq + Clone + Debug;
type GroupId: Debug + Hash + Ord + Eq + Clone;
/// A signature type.
type Signature: Eq + Clone + Debug;
type Signature: Debug + Eq + Clone;
/// Candidate type. In practice this will be a candidate receipt.
type Candidate: Debug + Ord + Eq + Clone;
/// get the digest of a candidate.
fn candidate_digest(&self, candidate: &Self::Candidate) -> Self::Digest;
fn candidate_digest(candidate: &Self::Candidate) -> Self::Digest;
/// get the group of a candidate.
fn candidate_group(&self, candidate: &Self::Candidate) -> Self::GroupId;
fn candidate_group(candidate: &Self::Candidate) -> Self::GroupId;
/// Whether a validator is a member of a group.
/// Members are meant to submit candidates and vote on validity.
@@ -259,13 +259,22 @@ pub fn create<C: Context>() -> Table<C> {
}
/// Stores votes
#[derive(Default)]
pub struct Table<C: Context> {
validator_data: HashMap<C::ValidatorId, ValidatorData<C>>,
detected_misbehavior: HashMap<C::ValidatorId, <C as ResolveMisbehavior>::Misbehavior>,
candidate_votes: HashMap<C::Digest, CandidateData<C>>,
}
impl<C: Context> Default for Table<C> {
fn default() -> Self {
Table {
validator_data: HashMap::new(),
detected_misbehavior: HashMap::new(),
candidate_votes: HashMap::new(),
}
}
}
impl<C: Context> Table<C> {
/// Produce a set of proposed candidates.
///
@@ -385,7 +394,7 @@ impl<C: Context> Table<C> {
candidate: C::Candidate,
signature: C::Signature,
) -> (Option<<C as ResolveMisbehavior>::Misbehavior>, Option<Summary<C::Digest, C::GroupId>>) {
let group = context.candidate_group(&candidate);
let group = C::candidate_group(&candidate);
if !context.is_member_of(&from, &group) {
return (
Some(Misbehavior::UnauthorizedStatement(UnauthorizedStatement {
@@ -400,7 +409,7 @@ impl<C: Context> Table<C> {
}
// check that validator hasn't already specified another candidate.
let digest = context.candidate_digest(&candidate);
let digest = C::candidate_digest(&candidate);
let new_proposal = match self.validator_data.entry(from.clone()) {
Entry::Occupied(mut occ) => {
@@ -608,11 +617,11 @@ mod tests {
type GroupId = GroupId;
type Signature = Signature;
fn candidate_digest(&self, candidate: &Candidate) -> Digest {
fn candidate_digest(candidate: &Candidate) -> Digest {
Digest(candidate.1)
}
fn candidate_group(&self, candidate: &Candidate) -> GroupId {
fn candidate_group(candidate: &Candidate) -> GroupId {
GroupId(candidate.0)
}