Minor API and documentation improvements.

This commit is contained in:
Andreas Fackler 2018-10-24 12:26:43 +02:00 committed by Andreas Fackler
parent 26ef33e4d1
commit 3981b37fa3
9 changed files with 81 additions and 19 deletions

View File

@ -77,7 +77,7 @@ impl<N: NodeIdT> DistAlgorithm for BinaryAgreement<N> {
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<N>> {
self.handle_input(input)
self.propose(input)
}
/// Receive input from a remote node.
@ -120,9 +120,14 @@ impl<N: NodeIdT> BinaryAgreement<N> {
})
}
/// Sets the input value for Binary Agreement.
pub fn handle_input(&mut self, input: bool) -> Result<Step<N>> {
if !self.can_input() {
/// Proposes a boolean value for Binary Agreement.
///
/// If more than two thirds of validators propose the same value, that will eventually be
/// output. Otherwise either output is possible.
///
/// Note that if `can_propose` returns `false`, it is already too late to affect the outcome.
pub fn propose(&mut self, input: bool) -> Result<Step<N>> {
if !self.can_propose() {
return Ok(Step::default());
}
// Set the initial estimated value to the input value.
@ -132,7 +137,9 @@ impl<N: NodeIdT> BinaryAgreement<N> {
self.handle_sbvb_step(sbvb_step)
}
/// Handles an incoming message.
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, msg: Message) -> Result<Step<N>> {
let Message { epoch, content } = msg;
if self.decision.is_some() || (epoch < self.epoch && content.can_expire()) {
@ -150,7 +157,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
/// Whether we can still input a value. It is not an error to input if this returns `false`,
/// but it will have no effect on the outcome.
pub fn can_input(&self) -> bool {
pub fn can_propose(&self) -> bool {
self.epoch == 0 && self.estimated.is_none()
}
@ -365,7 +372,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
// Invoke the coin.
let ts_step = match self.coin_state {
CoinState::Decided(_) => return Ok(Step::default()), // Coin has already decided.
CoinState::InProgress(ref mut ts) => ts.handle_input(()).map_err(Error::InvokeCoin)?,
CoinState::InProgress(ref mut ts) => ts.sign().map_err(Error::InvokeCoin)?,
};
let mut step = self.on_coin_step(ts_step)?;
step.extend(self.try_update_epoch()?);

View File

@ -21,6 +21,8 @@ pub struct Broadcast<N> {
proposer_id: N,
data_shard_num: usize,
coding: Coding,
/// If we are the proposer: whether we have already sent the `Value` messages with the shards.
value_sent: bool,
/// Whether we have already multicast `Echo`.
echo_sent: bool,
/// Whether we have already multicast `Ready`.
@ -74,6 +76,7 @@ impl<N: NodeIdT> Broadcast<N> {
proposer_id,
data_shard_num,
coding,
value_sent: false,
echo_sent: false,
ready_sent: false,
decided: false,
@ -87,6 +90,10 @@ impl<N: NodeIdT> Broadcast<N> {
if *self.netinfo.our_id() != self.proposer_id {
return Err(Error::InstanceCannotPropose);
}
if self.value_sent {
return Err(Error::MultipleInputs);
}
self.value_sent = true;
// Split the value into chunks/shards, encode them with erasure codes.
// Assemble a Merkle tree from data and parity shards. Take all proofs
// from this tree and send them, each to its own node.
@ -96,7 +103,9 @@ impl<N: NodeIdT> Broadcast<N> {
Ok(step)
}
/// Handles an incoming message.
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
if !self.netinfo.is_node_validator(sender_id) {
return Err(Error::UnknownSender);

View File

@ -16,6 +16,8 @@ pub enum Error {
CodingReconstructShardsTrivialReedSolomon(#[cause] rse::Error),
#[fail(display = "Instance cannot propose")]
InstanceCannotPropose,
#[fail(display = "Multiple inputs received")]
MultipleInputs,
#[fail(display = "Not implemented")]
NotImplemented,
#[fail(display = "Proof construction failed")]

View File

@ -93,6 +93,11 @@ where
}
/// Proposes a contribution in the current epoch.
///
/// Returns an error if we already made a proposal in this epoch.
///
/// If we are the only validator, this will immediately output a batch, containing our
/// proposal.
pub fn propose(&mut self, contrib: C) -> Result<Step<C, N>> {
let key_gen_messages = self
.key_gen_msg_buffer
@ -111,6 +116,9 @@ where
}
/// Casts a vote to change the set of validators.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_for(&mut self, change: Change<N>) -> Result<Step<C, N>> {
if !self.netinfo.is_validator() {
return Ok(Step::default()); // TODO: Return an error?
@ -120,7 +128,9 @@ where
Ok(Target::All.message(msg).into())
}
/// Handles an incoming message.
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<C, N>> {
let epoch = message.start_epoch();
if epoch < self.start_epoch {

View File

@ -77,7 +77,12 @@ where
HoneyBadgerBuilder::new(netinfo)
}
/// Proposes a new item in the current epoch.
/// Proposes a contribution in the current epoch.
///
/// Returns an error if we already made a proposal in this epoch.
///
/// If we are the only validator, this will immediately output a batch, containing our
/// proposal.
pub fn propose(&mut self, proposal: &C) -> Result<Step<C, N>> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
@ -97,7 +102,9 @@ where
}
/// Handles a message received from `sender_id`.
fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<C, N>> {
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<C, N>> {
if !self.netinfo.is_node_validator(sender_id) {
return Err(ErrorKind::UnknownSender.into());
}

View File

@ -234,12 +234,22 @@ where
}
/// Adds a transaction to the queue.
///
/// This can be called at any time to append to the transaction queue. The new transaction will
/// be proposed in some future epoch.
///
/// If no proposal has yet been made for the current epoch, this may trigger one. In this case,
/// a nonempty step will returned, with the corresponding messages. (Or, if we are the only
/// validator, even with the completed batch as an output.)
pub fn push_transaction(&mut self, tx: T) -> Result<Step<T, N, Q>> {
self.queue.extend(iter::once(tx));
self.propose()
}
/// Casts a vote to change the set of validators.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_for(&mut self, change: Change<N>) -> Result<Step<T, N, Q>> {
let mut step = self
.dyn_hb
@ -250,7 +260,9 @@ where
Ok(step)
}
/// Handles an incoming message.
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<T, N, Q>> {
let mut step = self
.dyn_hb

View File

@ -95,7 +95,7 @@ impl<N: NodeIdT + Rand> DistAlgorithm for Subset<N> {
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<N>> {
self.send_proposed_value(input)
self.propose(input)
}
fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<N>> {
@ -118,6 +118,10 @@ pub enum SubsetOutput<N> {
}
impl<N: NodeIdT + Rand> Subset<N> {
/// Creates a new `Subset` instance with the given session identifier.
///
/// If multiple `Subset`s are instantiated within a single network, they must use different
/// session identifiers to foil replay attacks.
pub fn new(netinfo: Arc<NetworkInfo<N>>, session_id: u64) -> Result<Self> {
// Create all broadcast instances.
let mut broadcast_instances: BTreeMap<N, Broadcast<N>> = BTreeMap::new();
@ -149,9 +153,10 @@ impl<N: NodeIdT + Rand> Subset<N> {
})
}
/// Subset input message handler. It receives a value for broadcast
/// and redirects it to the corresponding broadcast instance.
pub fn send_proposed_value(&mut self, value: ProposedValue) -> Result<Step<N>> {
/// Proposes a value for the subset.
///
/// Returns an error if we already made a proposal.
pub fn propose(&mut self, value: ProposedValue) -> Result<Step<N>> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
}
@ -160,7 +165,9 @@ impl<N: NodeIdT + Rand> Subset<N> {
self.process_broadcast(&id, |bc| bc.handle_input(value))
}
/// Handles an incoming message.
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<N>> {
match message {
Message::Broadcast(p_id, b_msg) => self.handle_broadcast(sender_id, &p_id, b_msg),

View File

@ -116,7 +116,11 @@ impl<N: NodeIdT> ThresholdDecryption<N> {
self.shares.keys()
}
/// Handles an incoming message. If we have collected enough shares, outputs the plaintext.
/// Handles a message with a decryption share received from `sender_id`.
///
/// This must be called with every message we receive from another node.
///
/// If we have collected enough, returns the decrypted message.
pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
if self.terminated {
return Ok(Step::default()); // Don't waste time on redundant shares.

View File

@ -120,7 +120,11 @@ impl<N: NodeIdT> ThresholdSign<N> {
Ok(step)
}
/// Handles an incoming share. If we have collected enough, returns the full signature.
/// Handles a message with a signature share received from `sender_id`.
///
/// This must be called with every message we receive from another node.
///
/// If we have collected enough, returns the full signature.
pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
if self.terminated {
return Ok(Step::default());