Make the BA session ID generic.

This commit is contained in:
Andreas Fackler 2018-10-24 09:42:59 +02:00
parent 6375decbc0
commit 506f031d93
5 changed files with 69 additions and 38 deletions

View File

@ -1,6 +1,10 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use bincode;
use serde::Serialize;
use super::bool_multimap::BoolMultimap; use super::bool_multimap::BoolMultimap;
use super::bool_set::BoolSet; use super::bool_set::BoolSet;
use super::sbv_broadcast::{self, SbvBroadcast}; use super::sbv_broadcast::{self, SbvBroadcast};
@ -39,10 +43,13 @@ impl<N> From<bool> for CoinState<N> {
pub struct BinaryAgreement<N> { pub struct BinaryAgreement<N> {
/// Shared network information. /// Shared network information.
netinfo: Arc<NetworkInfo<N>>, netinfo: Arc<NetworkInfo<N>>,
/// Session ID, e.g, the Honey Badger algorithm epoch. // We store the session ID twice: once in its serialized form, to create the `ThresholdSign`
session_id: u64, // nonces, and once boxed, for debug output.
/// The ID of the proposer of the value for this Binary Agreement instance. /// Session identifier, to prevent replaying messages in other instances,
proposer_id: N, /// e.g, the Honey Badger algorithm epoch plus the proposer ID.
session_id: Box<Debug + Send + Sync>,
/// The serialized session identifier.
ser_session_id: Vec<u8>,
/// Binary Agreement algorithm epoch. /// Binary Agreement algorithm epoch.
epoch: u32, epoch: u32,
/// This epoch's Synchronized Binary Value Broadcast instance. /// This epoch's Synchronized Binary Value Broadcast instance.
@ -96,18 +103,16 @@ impl<N: NodeIdT> DistAlgorithm for BinaryAgreement<N> {
} }
impl<N: NodeIdT> BinaryAgreement<N> { impl<N: NodeIdT> BinaryAgreement<N> {
/// Creates a new `BinaryAgreement` instance. The `session_id` and `proposer_id` are used to /// Creates a new `BinaryAgreement` instance with the given session identifier, to prevent
/// uniquely identify this instance: its messages cannot be replayed in an instance with /// replaying messages in other instances.
/// different values. pub fn new<T>(netinfo: Arc<NetworkInfo<N>>, session_id: T) -> Result<Self>
// TODO: Use a generic type argument for that instead of something `Subset`-specific. where
pub fn new(netinfo: Arc<NetworkInfo<N>>, session_id: u64, proposer_id: N) -> Result<Self> { T: Serialize + Sync + Send + Debug + 'static,
if !netinfo.is_node_validator(&proposer_id) { {
return Err(Error::UnknownProposer);
}
Ok(BinaryAgreement { Ok(BinaryAgreement {
netinfo: netinfo.clone(), netinfo: netinfo.clone(),
session_id, ser_session_id: bincode::serialize(&session_id)?,
proposer_id, session_id: Box::new(session_id),
epoch: 0, epoch: 0,
sbv_broadcast: SbvBroadcast::new(netinfo), sbv_broadcast: SbvBroadcast::new(netinfo),
received_conf: BTreeMap::new(), received_conf: BTreeMap::new(),
@ -127,7 +132,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
} }
// Set the initial estimated value to the input value. // Set the initial estimated value to the input value.
self.estimated = Some(input); self.estimated = Some(input);
debug!("{:?}/{:?} Input {}", self.our_id(), self.proposer_id, input); debug!("{:?}/{:?} Input {}", self.our_id(), self.session_id, input);
let sbvb_step = self.sbv_broadcast.handle_input(input)?; let sbvb_step = self.sbv_broadcast.handle_input(input)?;
self.handle_sbvb_step(sbvb_step) self.handle_sbvb_step(sbvb_step)
} }
@ -316,20 +321,19 @@ impl<N: NodeIdT> BinaryAgreement<N> {
/// Creates the initial coin state for the current epoch, i.e. sets it to the predetermined /// Creates the initial coin state for the current epoch, i.e. sets it to the predetermined
/// value, or initializes a `ThresholdSign` instance. /// value, or initializes a `ThresholdSign` instance.
fn coin_state(&self) -> CoinState<N> { fn coin_state(&self) -> Result<CoinState<N>> {
match self.epoch % 3 { Ok(match self.epoch % 3 {
0 => CoinState::Decided(true), 0 => CoinState::Decided(true),
1 => CoinState::Decided(false), 1 => CoinState::Decided(false),
_ => { _ => {
let nonce = Nonce::new( let nonce = Nonce::new(
self.netinfo.invocation_id().as_ref(), self.netinfo.invocation_id().as_ref(),
self.session_id, &self.ser_session_id,
self.netinfo.node_index(&self.proposer_id).unwrap(),
self.epoch, self.epoch,
); )?;
CoinState::InProgress(Box::new(ThresholdSign::new(self.netinfo.clone(), nonce))) CoinState::InProgress(Box::new(ThresholdSign::new(self.netinfo.clone(), nonce)))
} }
} })
} }
/// Decides on a value and broadcasts a `Term` message with that value. /// Decides on a value and broadcasts a `Term` message with that value.
@ -345,7 +349,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
debug!( debug!(
"{:?}/{:?} (is_validator: {}) decision: {}", "{:?}/{:?} (is_validator: {}) decision: {}",
self.netinfo.our_id(), self.netinfo.our_id(),
self.proposer_id, self.session_id,
self.netinfo.is_validator(), self.netinfo.is_validator(),
b b
); );
@ -387,11 +391,11 @@ impl<N: NodeIdT> BinaryAgreement<N> {
} }
self.conf_values = None; self.conf_values = None;
self.epoch += 1; self.epoch += 1;
self.coin_state = self.coin_state(); self.coin_state = self.coin_state()?;
debug!( debug!(
"{:?} BinaryAgreement instance {:?} started epoch {}, {} terminated", "{:?} BinaryAgreement instance {:?} started epoch {}, {} terminated",
self.netinfo.our_id(), self.netinfo.our_id(),
self.proposer_id, self.session_id,
self.epoch, self.epoch,
self.received_conf.len(), self.received_conf.len(),
); );

View File

@ -68,6 +68,10 @@ mod bool_multimap;
pub mod bool_set; pub mod bool_set;
mod sbv_broadcast; mod sbv_broadcast;
use std::io;
use bincode;
use byteorder::{BigEndian, WriteBytesExt};
use rand; use rand;
use self::bool_set::BoolSet; use self::bool_set::BoolSet;
@ -82,8 +86,23 @@ pub enum Error {
HandleThresholdSign(threshold_sign::Error), HandleThresholdSign(threshold_sign::Error),
#[fail(display = "Error invoking the common coin: {}", _0)] #[fail(display = "Error invoking the common coin: {}", _0)]
InvokeCoin(threshold_sign::Error), InvokeCoin(threshold_sign::Error),
#[fail(display = "Unknown proposer")] // Strings because `io` and `bincode` errors lack `Eq` and `Clone`.
UnknownProposer, #[fail(display = "Error writing epoch for nonce: {}", _0)]
Io(String),
#[fail(display = "Error serializing session ID for nonce: {}", _0)]
Serialize(String),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(format!("{:?}", err))
}
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Error {
Error::Io(format!("{:?}", err))
}
} }
/// An Binary Agreement result. /// An Binary Agreement result.
@ -151,14 +170,13 @@ struct Nonce(Vec<u8>);
impl Nonce { impl Nonce {
pub fn new( pub fn new(
invocation_id: &[u8], invocation_id: &[u8],
session_id: u64, session_id: &[u8],
proposer_id: usize,
binary_agreement_epoch: u32, binary_agreement_epoch: u32,
) -> Self { ) -> Result<Self> {
Nonce(Vec::from(format!( let mut vec = invocation_id.to_vec();
"Nonce for Honey Badger {:?}@{}:{}:{}", vec.write_u32::<BigEndian>(binary_agreement_epoch)?;
invocation_id, session_id, binary_agreement_epoch, proposer_id vec.extend(session_id);
))) Ok(Nonce(vec))
} }
} }

View File

@ -116,7 +116,8 @@ impl<N: NodeIdT> NetworkInfo<N> {
&self.public_keys &self.public_keys
} }
/// The index of a node in a canonical numbering of all nodes. /// The index of a node in a canonical numbering of all nodes. This is the index where the
/// node appears in `all_ids`.
pub fn node_index(&self, id: &N) -> Option<usize> { pub fn node_index(&self, id: &N) -> Option<usize> {
self.node_indices.get(id).cloned() self.node_indices.get(id).cloned()
} }

View File

@ -131,11 +131,13 @@ impl<N: NodeIdT + Rand> Subset<N> {
// Create all Binary Agreement instances. // Create all Binary Agreement instances.
let mut ba_instances: BTreeMap<N, BinaryAgreement<N>> = BTreeMap::new(); let mut ba_instances: BTreeMap<N, BinaryAgreement<N>> = BTreeMap::new();
for proposer_id in netinfo.all_ids() { for (proposer_idx, proposer_id) in netinfo.all_ids().enumerate() {
ba_instances.insert( ba_instances.insert(
proposer_id.clone(), proposer_id.clone(),
BinaryAgreement::new(netinfo.clone(), session_id, proposer_id.clone()) BinaryAgreement::new(
.map_err(Error::NewBinaryAgreement)?, netinfo.clone(),
BaSessionId(session_id, proposer_idx as u32),
).map_err(Error::NewBinaryAgreement)?,
); );
} }
@ -366,3 +368,9 @@ impl<N: NodeIdT + Rand> Subset<N> {
} }
} }
} }
/// A session identifier for a `BinaryAgreement` instance run as a `Subset` sub-algorithm. It
/// consists of the `Subset` instance's own session ID, and the index of the proposer whose
/// contribution this `BinaryAgreement` is about.
#[derive(Debug, Serialize)]
struct BaSessionId(u64, u32);

View File

@ -85,7 +85,7 @@ where
); );
let adversary = |_| new_adversary(num_good_nodes, num_faulty_nodes); let adversary = |_| new_adversary(num_good_nodes, num_faulty_nodes);
let new_ba = |netinfo: Arc<NetworkInfo<NodeId>>| { let new_ba = |netinfo: Arc<NetworkInfo<NodeId>>| {
BinaryAgreement::new(netinfo, 0, NodeId(0)).expect("Binary Agreement instance") BinaryAgreement::new(netinfo, 0).expect("Binary Agreement instance")
}; };
let network = TestNetwork::new(num_good_nodes, num_faulty_nodes, adversary, new_ba); let network = TestNetwork::new(num_good_nodes, num_faulty_nodes, adversary, new_ba);
test_binary_agreement(network, input); test_binary_agreement(network, input);