diff --git a/src/round.rs b/src/round.rs index 21861828..442c68ff 100644 --- a/src/round.rs +++ b/src/round.rs @@ -616,6 +616,11 @@ impl Round where self.voters.voter_by_index(self.round_number as usize % self.voters.len()) } + /// Return all imported prevotes. + pub fn prevotes(&self) -> Vec<(Id, Prevote, Signature)> { + self.prevote.votes() + } + /// Return all imported precommits. pub fn precommits(&self) -> Vec<(Id, Precommit, Signature)> { self.precommit.votes() diff --git a/src/testing.rs b/src/testing.rs index acd22f5c..749ce778 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -26,7 +26,7 @@ use tokio::timer::Delay; use parking_lot::Mutex; use futures::prelude::*; use futures::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use super::{Chain, Commit, Error, Equivocation, Message, Prevote, Precommit, SignedMessage}; +use super::{Chain, Commit, Error, Equivocation, Message, Prevote, Precommit, PrimaryPropose, SignedMessage}; pub const GENESIS_HASH: &str = "genesis"; const NULL_HASH: &str = "NULL"; @@ -215,7 +215,13 @@ impl crate::voter::Environment<&'static str, u32> for Environment { Box::new(Delay::new(now + delay).map_err(|_| panic!("Timer failed"))) } - fn completed(&self, _round: u64, _state: RoundState<&'static str, u32>) -> Result<(), Error> { + fn completed( + &self, + _round: u64, + _state: RoundState<&'static str, u32>, + _base: (&'static str, u32), + _votes: Vec>, + ) -> Result<(), Error> { Ok(()) } @@ -230,6 +236,18 @@ impl crate::voter::Environment<&'static str, u32> for Environment { Ok(()) } + fn proposed(&self, _round: u64, _propose: PrimaryPropose<&'static str, u32>) -> Result<(), Self::Error> { + Ok(()) + } + + fn prevoted(&self, _round: u64, _prevote: Prevote<&'static str, u32>) -> Result<(), Self::Error> { + Ok(()) + } + + fn precommitted(&self, _round: u64, _precommit: Precommit<&'static str, u32>) -> Result<(), Self::Error> { + Ok(()) + } + fn prevote_equivocation(&self, round: u64, equivocation: Equivocation, Signature>) { panic!("Encountered equivocation in round {}: {:?}", round, equivocation); } diff --git a/src/voter/mod.rs b/src/voter/mod.rs index 6ff16279..a80a5a11 100644 --- a/src/voter/mod.rs +++ b/src/voter/mod.rs @@ -37,8 +37,8 @@ use std::sync::Arc; use crate::round::State as RoundState; use crate::{ - Chain, Commit, CompactCommit, Equivocation, Message, Prevote, Precommit, SignedMessage, - BlockNumberOps, validate_commit + Chain, Commit, CompactCommit, Equivocation, Message, Prevote, Precommit, PrimaryPropose, + SignedMessage, BlockNumberOps, validate_commit }; use crate::voter_set::VoterSet; use past_rounds::PastRounds; @@ -87,9 +87,24 @@ pub trait Environment: Chain { /// commit messages that are sent (e.g. random value in [0, 1] seconds). fn round_commit_timer(&self) -> Self::Timer; + /// Note that we've done a primary proposal in the given round. + fn proposed(&self, round: u64, propose: PrimaryPropose) -> Result<(), Self::Error>; + + /// Note that we have prevoted in the given round. + fn prevoted(&self, round: u64, prevote: Prevote) -> Result<(), Self::Error>; + + /// Note that we have precommitted in the given round. + fn precommitted(&self, round: u64, precommit: Precommit) -> Result<(), Self::Error>; + /// Note that a round was completed. This is called when a round has been /// voted in. Should return an error when something fatal occurs. - fn completed(&self, round: u64, state: RoundState) -> Result<(), Self::Error>; + fn completed( + &self, + round: u64, + state: RoundState, + base: (H, N), + votes: Vec>, + ) -> Result<(), Self::Error>; /// Called when a block should be finalized. // TODO: make this a future that resolves when it's e.g. written to disk? @@ -554,7 +569,12 @@ impl, GlobalIn, GlobalOut> Voter>) -> Result<(), E::Error> { - self.env.completed(self.best_round.round_number(), self.best_round.round_state())?; + self.env.completed( + self.best_round.round_number(), + self.best_round.round_state(), + self.best_round.dag_base(), + self.best_round.votes(), + )?; let old_round_number = self.best_round.round_number(); @@ -577,7 +597,12 @@ impl, GlobalIn, GlobalOut> Voter) -> Result<(), E::Error> { - self.env.completed(prospective_round.round_number(), prospective_round.round_state())?; + self.env.completed( + prospective_round.round_number(), + prospective_round.round_state(), + prospective_round.dag_base(), + prospective_round.votes(), + )?; self.best_round = VotingRound::new( prospective_round.round_number() + 1, diff --git a/src/voter/voting_round.rs b/src/voter/voting_round.rs index 940578d2..3da1b252 100644 --- a/src/voter/voting_round.rs +++ b/src/voter/voting_round.rs @@ -256,6 +256,27 @@ impl> VotingRound where self.best_finalized.as_ref() } + /// Return all imported votes for the round (prevotes and precommits). + pub(super) fn votes(&self) -> Vec> { + let prevotes = self.votes.prevotes().into_iter().map(|(id, prevote, signature)| { + SignedMessage { + id, + signature, + message: Message::Prevote(prevote), + } + }); + + let precommits = self.votes.precommits().into_iter().map(|(id, precommit, signature)| { + SignedMessage { + id, + signature, + message: Message::Precommit(precommit), + } + }); + + prevotes.chain(precommits).collect() + } + fn process_incoming(&mut self) -> Result<(), E::Error> { while let Async::Ready(Some(incoming)) = self.incoming.poll()? { trace!(target: "afg", "Got incoming message"); @@ -303,12 +324,12 @@ impl> VotingRound where let should_send_primary = maybe_finalized.map_or(true, |f| last_round_estimate.1 < f.1); if should_send_primary { debug!(target: "afg", "Sending primary block hint for round {}", self.votes.number()); - self.outgoing.push(Message::PrimaryPropose( - PrimaryPropose { - target_hash: last_round_estimate.0, - target_number: last_round_estimate.1, - }) - ); + let primary = PrimaryPropose { + target_hash: last_round_estimate.0, + target_number: last_round_estimate.1, + }; + self.env.proposed(self.round_number(), primary.clone())?; + self.outgoing.push(Message::PrimaryPropose(primary)); self.state = Some(State::Proposed(prevote_timer, precommit_timer)); return Ok(()); @@ -342,6 +363,7 @@ impl> VotingRound where if self.voting.is_active() { if let Some(prevote) = self.construct_prevote(last_round_state)? { debug!(target: "afg", "Casting prevote for round {}", self.votes.number()); + self.env.prevoted(self.round_number(), prevote.clone())?; self.outgoing.push(Message::Prevote(prevote)); } } @@ -393,6 +415,7 @@ impl> VotingRound where if self.voting.is_active() { debug!(target: "afg", "Casting precommit for round {}", self.votes.number()); let precommit = self.construct_precommit(); + self.env.precommitted(self.round_number(), precommit.clone())?; self.outgoing.push(Message::Precommit(precommit)); } self.state = Some(State::Precommitted);