mirror of https://github.com/poanetwork/hbbft.git
Fixed merge conflict in readme.
This commit is contained in:
commit
817b2da962
|
@ -1,3 +1,6 @@
|
||||||
|
[![Build Status](https://travis-ci.com/poanetwork/hbbft.svg?branch=master)](https://travis-ci.com/poanetwork/hbbft)
|
||||||
|
[![Gitter](https://badges.gitter.im/poanetwork/hbbft.svg)](https://gitter.im/poanetwork/hbbft?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
|
|
||||||
# About
|
# About
|
||||||
|
|
||||||
An implementation of the paper
|
An implementation of the paper
|
||||||
|
@ -42,4 +45,3 @@ Once you have verified that the `protoc` binary is in your `$PATH`, you can
|
||||||
build `hbbft` using cargo:
|
build `hbbft` using cargo:
|
||||||
|
|
||||||
$ cargo build [--release]
|
$ cargo build [--release]
|
||||||
|
|
||||||
|
|
132
src/broadcast.rs
132
src/broadcast.rs
|
@ -94,7 +94,7 @@ impl Broadcast<messaging::NodeUid> {
|
||||||
message,
|
message,
|
||||||
}) => {
|
}) => {
|
||||||
if let Message::Broadcast(b) = message {
|
if let Message::Broadcast(b) = message {
|
||||||
self.on_remote_message(uid, &b, tx)
|
self.on_remote_message(uid, b, tx)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnexpectedMessage)
|
Err(Error::UnexpectedMessage)
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ impl Broadcast<messaging::NodeUid> {
|
||||||
fn on_remote_message(
|
fn on_remote_message(
|
||||||
&self,
|
&self,
|
||||||
uid: messaging::NodeUid,
|
uid: messaging::NodeUid,
|
||||||
message: &BroadcastMessage<ProposedValue>,
|
message: BroadcastMessage<ProposedValue>,
|
||||||
tx: &Sender<QMessage>,
|
tx: &Sender<QMessage>,
|
||||||
) -> Result<MessageLoopState, Error> {
|
) -> Result<MessageLoopState, Error> {
|
||||||
let (output, messages) = self.handle_broadcast_message(&uid, message)?;
|
let (output, messages) = self.handle_broadcast_message(&uid, message)?;
|
||||||
|
@ -207,22 +207,17 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
// Split the value into chunks/shards, encode them with erasure codes.
|
// Split the value into chunks/shards, encode them with erasure codes.
|
||||||
// Assemble a Merkle tree from data and parity shards. Take all proofs
|
// Assemble a Merkle tree from data and parity shards. Take all proofs
|
||||||
// from this tree and send them, each to its own node.
|
// from this tree and send them, each to its own node.
|
||||||
self.send_shards(value)
|
self.send_shards(value).map(|(proof, remote_messages)| {
|
||||||
.map_err(Error::from)
|
// Record the first proof as if it were sent by the node to itself.
|
||||||
.map(|(proof, remote_messages)| {
|
let h = proof.root_hash.clone();
|
||||||
// Record the first proof as if it were sent by the node to
|
// Save the leaf value for reconstructing the tree later.
|
||||||
// itself.
|
state.leaf_values[index_of_proof(&proof)] =
|
||||||
let h = proof.root_hash.clone();
|
Some(proof.value.clone().into_boxed_slice());
|
||||||
if proof.validate(h.as_slice()) {
|
state.leaf_values_num += 1;
|
||||||
// Save the leaf value for reconstructing the tree later.
|
state.root_hash = Some(h);
|
||||||
state.leaf_values[index_of_proof(&proof)] =
|
|
||||||
Some(proof.value.clone().into_boxed_slice());
|
|
||||||
state.leaf_values_num += 1;
|
|
||||||
state.root_hash = Some(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
remote_messages
|
remote_messages
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn our_id(&self) -> &NodeUid {
|
pub fn our_id(&self) -> &NodeUid {
|
||||||
|
@ -247,8 +242,7 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
);
|
);
|
||||||
// Insert the length of `v` so it can be decoded without the padding.
|
// Insert the length of `v` so it can be decoded without the padding.
|
||||||
let payload_len = value.len() as u8;
|
let payload_len = value.len() as u8;
|
||||||
value.insert(0, payload_len); // TODO: Handle messages larger than 255
|
value.insert(0, payload_len); // TODO: Handle messages larger than 255 bytes.
|
||||||
// bytes.
|
|
||||||
let value_len = value.len();
|
let value_len = value.len();
|
||||||
// Size of a Merkle tree leaf value, in bytes.
|
// Size of a Merkle tree leaf value, in bytes.
|
||||||
let shard_len = if value_len % data_shard_num > 0 {
|
let shard_len = if value_len % data_shard_num > 0 {
|
||||||
|
@ -265,17 +259,12 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
// Divide the vector into chunks/shards.
|
// Divide the vector into chunks/shards.
|
||||||
let shards_iter = value.chunks_mut(shard_len);
|
let shards_iter = value.chunks_mut(shard_len);
|
||||||
// Convert the iterator over slices into a vector of slices.
|
// Convert the iterator over slices into a vector of slices.
|
||||||
let mut shards: Vec<&mut [u8]> = Vec::new();
|
let mut shards: Vec<&mut [u8]> = shards_iter.collect();
|
||||||
for s in shards_iter {
|
|
||||||
shards.push(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Shards before encoding: {:?}", shards);
|
debug!("Shards before encoding: {:?}", shards);
|
||||||
|
|
||||||
// Construct the parity chunks/shards
|
// Construct the parity chunks/shards
|
||||||
self.coding
|
self.coding.encode(&mut shards)?;
|
||||||
.encode(shards.as_mut_slice())
|
|
||||||
.map_err(Error::from)?;
|
|
||||||
|
|
||||||
debug!("Shards: {:?}", shards);
|
debug!("Shards: {:?}", shards);
|
||||||
|
|
||||||
|
@ -290,19 +279,21 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
let mut outgoing = VecDeque::new();
|
let mut outgoing = VecDeque::new();
|
||||||
|
|
||||||
// Send each proof to a node.
|
// Send each proof to a node.
|
||||||
|
// TODO: This generates the wrong proof if a leaf occurs more than once. Consider using the
|
||||||
|
// `merkle_light` crate instead.
|
||||||
for (leaf_value, uid) in mtree.iter().zip(self.all_uids.clone()) {
|
for (leaf_value, uid) in mtree.iter().zip(self.all_uids.clone()) {
|
||||||
let proof = mtree.gen_proof(leaf_value.to_vec());
|
let proof = mtree
|
||||||
if let Some(proof) = proof {
|
.gen_proof(leaf_value.to_vec())
|
||||||
if uid == self.our_id {
|
.ok_or(Error::ProofConstructionFailed)?;
|
||||||
// The proof is addressed to this node.
|
if uid == self.our_id {
|
||||||
result = Ok(proof);
|
// The proof is addressed to this node.
|
||||||
} else {
|
result = Ok(proof);
|
||||||
// Rest of the proofs are sent to remote nodes.
|
} else {
|
||||||
outgoing.push_back(TargetedBroadcastMessage {
|
// Rest of the proofs are sent to remote nodes.
|
||||||
target: BroadcastTarget::Node(uid),
|
outgoing.push_back(TargetedBroadcastMessage {
|
||||||
message: BroadcastMessage::Value(proof),
|
target: BroadcastTarget::Node(uid),
|
||||||
});
|
message: BroadcastMessage::Value(proof),
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,13 +304,13 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
pub fn handle_broadcast_message(
|
pub fn handle_broadcast_message(
|
||||||
&self,
|
&self,
|
||||||
sender_id: &NodeUid,
|
sender_id: &NodeUid,
|
||||||
message: &BroadcastMessage<ProposedValue>,
|
message: BroadcastMessage<ProposedValue>,
|
||||||
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
||||||
let state = self.state.write().unwrap();
|
let state = self.state.write().unwrap();
|
||||||
match message {
|
match message {
|
||||||
BroadcastMessage::Value(p) => self.handle_value(sender_id, p, state),
|
BroadcastMessage::Value(p) => self.handle_value(sender_id, p, state),
|
||||||
BroadcastMessage::Echo(p) => self.handle_echo(p, state),
|
BroadcastMessage::Echo(p) => self.handle_echo(p, state),
|
||||||
BroadcastMessage::Ready(ref hash) => self.handle_ready(hash, state),
|
BroadcastMessage::Ready(hash) => self.handle_ready(hash, state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +318,7 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
fn handle_value(
|
fn handle_value(
|
||||||
&self,
|
&self,
|
||||||
sender_id: &NodeUid,
|
sender_id: &NodeUid,
|
||||||
p: &Proof<ProposedValue>,
|
p: Proof<ProposedValue>,
|
||||||
mut state: RwLockWriteGuard<BroadcastState>,
|
mut state: RwLockWriteGuard<BroadcastState>,
|
||||||
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
||||||
if *sender_id != self.proposer_id {
|
if *sender_id != self.proposer_id {
|
||||||
|
@ -343,13 +334,12 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref h) = state.root_hash.clone() {
|
if state.root_hash.as_ref().map_or(false, |h| p.validate(h)) {
|
||||||
if p.validate(h.as_slice()) {
|
// TODO: Should messages failing this be echoed at all?
|
||||||
// Save the leaf value for reconstructing the tree
|
// Save the leaf value for reconstructing the tree later.
|
||||||
// later.
|
let idx = index_of_proof(&p);
|
||||||
state.leaf_values[index_of_proof(&p)] = Some(p.value.clone().into_boxed_slice());
|
state.leaf_values[idx] = Some(p.value.clone().into_boxed_slice());
|
||||||
state.leaf_values_num += 1;
|
state.leaf_values_num += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue a broadcast of an echo of this proof.
|
// Enqueue a broadcast of an echo of this proof.
|
||||||
|
@ -364,7 +354,7 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
/// Handles a received echo and verifies the proof it contains.
|
/// Handles a received echo and verifies the proof it contains.
|
||||||
fn handle_echo(
|
fn handle_echo(
|
||||||
&self,
|
&self,
|
||||||
p: &Proof<ProposedValue>,
|
p: Proof<ProposedValue>,
|
||||||
mut state: RwLockWriteGuard<BroadcastState>,
|
mut state: RwLockWriteGuard<BroadcastState>,
|
||||||
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
||||||
if state.root_hash.is_none() {
|
if state.root_hash.is_none() {
|
||||||
|
@ -389,9 +379,9 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
}
|
}
|
||||||
|
|
||||||
state.echo_num += 1;
|
state.echo_num += 1;
|
||||||
// Save the leaf value for reconstructing the
|
// Save the leaf value for reconstructing the tree later.
|
||||||
// tree later.
|
let idx = index_of_proof(&p);
|
||||||
state.leaf_values[index_of_proof(&p)] = Some(p.value.clone().into_boxed_slice());
|
state.leaf_values[idx] = Some(p.value.into_boxed_slice());
|
||||||
state.leaf_values_num += 1;
|
state.leaf_values_num += 1;
|
||||||
|
|
||||||
// Upon receiving 2f + 1 matching READY(h)
|
// Upon receiving 2f + 1 matching READY(h)
|
||||||
|
@ -401,6 +391,7 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
return Ok((None, VecDeque::new()));
|
return Ok((None, VecDeque::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Only decode once. Don't repeat for every ECHO message.
|
||||||
let value = decode_from_shards(
|
let value = decode_from_shards(
|
||||||
&mut state.leaf_values,
|
&mut state.leaf_values,
|
||||||
&self.coding,
|
&self.coding,
|
||||||
|
@ -408,9 +399,8 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
&h,
|
&h,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if state.ready_to_decode
|
if state.ready_to_decode && !state.has_output {
|
||||||
&& state.leaf_values_num >= self.num_nodes - 2 * self.num_faulty_nodes
|
state.has_output = true;
|
||||||
{
|
|
||||||
return Ok((Some(value), VecDeque::new()));
|
return Ok((Some(value), VecDeque::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,19 +412,20 @@ impl<NodeUid: Eq + Hash + Debug + Clone> Broadcast<NodeUid> {
|
||||||
state.ready_sent = true;
|
state.ready_sent = true;
|
||||||
let msg = TargetedBroadcastMessage {
|
let msg = TargetedBroadcastMessage {
|
||||||
target: BroadcastTarget::All,
|
target: BroadcastTarget::All,
|
||||||
message: BroadcastMessage::Ready(h.to_owned()),
|
message: BroadcastMessage::Ready(h.clone()),
|
||||||
};
|
};
|
||||||
let (output, ready_msgs) = self.handle_ready(&h, state)?;
|
let (output, ready_msgs) = self.handle_ready(h, state)?;
|
||||||
Ok((output, iter::once(msg).chain(ready_msgs).collect()))
|
Ok((output, iter::once(msg).chain(ready_msgs).collect()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ready(
|
fn handle_ready(
|
||||||
&self,
|
&self,
|
||||||
hash: &[u8],
|
hash: Vec<u8>,
|
||||||
mut state: RwLockWriteGuard<BroadcastState>,
|
mut state: RwLockWriteGuard<BroadcastState>,
|
||||||
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
) -> Result<(Option<ProposedValue>, MessageQueue<NodeUid>), Error> {
|
||||||
// Update the number Ready has been received with this hash.
|
// Update the number Ready has been received with this hash.
|
||||||
*state.readys.entry(hash.to_vec()).or_insert(1) += 1;
|
// TODO: Don't accept multiple ready messages from the same node.
|
||||||
|
*state.readys.entry(hash).or_insert(1) += 1;
|
||||||
|
|
||||||
// Check that the root hash matches.
|
// Check that the root hash matches.
|
||||||
let h = if let Some(h) = state.root_hash.clone() {
|
let h = if let Some(h) = state.root_hash.clone() {
|
||||||
|
@ -862,7 +853,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_from_shards<T>(
|
fn decode_from_shards<T>(
|
||||||
leaf_values: &mut Vec<Option<Box<[u8]>>>,
|
leaf_values: &mut [Option<Box<[u8]>>],
|
||||||
coding: &ReedSolomon,
|
coding: &ReedSolomon,
|
||||||
data_shard_num: usize,
|
data_shard_num: usize,
|
||||||
root_hash: &[u8],
|
root_hash: &[u8],
|
||||||
|
@ -870,19 +861,16 @@ fn decode_from_shards<T>(
|
||||||
where
|
where
|
||||||
T: Clone + Debug + Hashable + Send + Sync + From<Vec<u8>> + Into<Vec<u8>>,
|
T: Clone + Debug + Hashable + Send + Sync + From<Vec<u8>> + Into<Vec<u8>>,
|
||||||
{
|
{
|
||||||
// Try to interpolate the Merkle tree using the Reed-Solomon erasure coding
|
// Try to interpolate the Merkle tree using the Reed-Solomon erasure coding scheme.
|
||||||
// scheme.
|
coding.reconstruct_shards(leaf_values)?;
|
||||||
coding.reconstruct_shards(leaf_values.as_mut_slice())?;
|
|
||||||
|
|
||||||
// Recompute the Merkle tree root.
|
// Recompute the Merkle tree root.
|
||||||
//
|
|
||||||
// Collect shards for tree construction.
|
// Collect shards for tree construction.
|
||||||
let mut shards: Vec<ProposedValue> = Vec::new();
|
let shards: Vec<ProposedValue> = leaf_values
|
||||||
for l in leaf_values.iter() {
|
.iter()
|
||||||
if let Some(ref v) = *l {
|
.filter_map(|l| l.as_ref().map(|v| v.to_vec()))
|
||||||
shards.push(v.to_vec());
|
.collect();
|
||||||
}
|
|
||||||
}
|
|
||||||
// Construct the Merkle tree.
|
// Construct the Merkle tree.
|
||||||
let mtree = MerkleTree::from_vec(&::ring::digest::SHA256, shards);
|
let mtree = MerkleTree::from_vec(&::ring::digest::SHA256, shards);
|
||||||
// If the root hash of the reconstructed tree does not match the one
|
// If the root hash of the reconstructed tree does not match the one
|
||||||
|
@ -959,6 +947,8 @@ fn index_of_path(mut path: Vec<bool>) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the Merkle tree leaf index of a value in a given proof.
|
/// Computes the Merkle tree leaf index of a value in a given proof.
|
||||||
fn index_of_proof(p: &Proof<ProposedValue>) -> usize {
|
// TODO: This currently only works if the number of leaves is a power of two. With the
|
||||||
|
// `merkle_light` crate, it might not even be needed, though.
|
||||||
|
pub fn index_of_proof<T>(p: &Proof<T>) -> usize {
|
||||||
index_of_path(path_of_lemma(&p.lemma))
|
index_of_path(path_of_lemma(&p.lemma))
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ impl<NodeUid: Clone + Debug + Display + Eq + Hash + Ord> CommonSubset<NodeUid> {
|
||||||
let input_result = {
|
let input_result = {
|
||||||
if let Some(broadcast_instance) = self.broadcast_instances.get(&uid) {
|
if let Some(broadcast_instance) = self.broadcast_instances.get(&uid) {
|
||||||
broadcast_instance
|
broadcast_instance
|
||||||
.handle_broadcast_message(&uid, &bmessage)
|
.handle_broadcast_message(&uid, bmessage)
|
||||||
.map(|(value, queue)| {
|
.map(|(value, queue)| {
|
||||||
instance_result = value;
|
instance_result = value;
|
||||||
queue.into_iter().map(Output::Broadcast).collect()
|
queue.into_iter().map(Output::Broadcast).collect()
|
||||||
|
|
|
@ -53,9 +53,10 @@ impl<'a, T: Send + Sync + fmt::Debug> fmt::Debug for HexProof<'a, T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Proof {{ algorithm: {:?}, root_hash: {:?}, lemma: .., value: {:?} }}",
|
"Proof {{ algorithm: {:?}, root_hash: {:?}, lemma for leaf #{}, value: {:?} }}",
|
||||||
self.0.algorithm,
|
self.0.algorithm,
|
||||||
HexBytes(&self.0.root_hash),
|
HexBytes(&self.0.root_hash),
|
||||||
|
::broadcast::index_of_proof(self.0),
|
||||||
self.0.value
|
self.0.value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +76,7 @@ impl<T: Send + Sync + fmt::Debug> fmt::Debug for BroadcastMessage<T> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum AgreementMessage {
|
pub enum AgreementMessage {
|
||||||
BVal(bool),
|
BVal(bool),
|
||||||
Aux(bool)
|
Aux(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send + Sync> Message<T> {
|
impl<T: Send + Sync> Message<T> {
|
||||||
|
@ -198,7 +199,7 @@ impl AgreementMessage {
|
||||||
let mut p = AgreementProto::new();
|
let mut p = AgreementProto::new();
|
||||||
match self {
|
match self {
|
||||||
AgreementMessage::BVal(b) => p.set_bval(b),
|
AgreementMessage::BVal(b) => p.set_bval(b),
|
||||||
AgreementMessage::Aux(b) => p.set_aux(b)
|
AgreementMessage::Aux(b) => p.set_aux(b),
|
||||||
}
|
}
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
@ -208,11 +209,9 @@ impl AgreementMessage {
|
||||||
pub fn from_proto(mp: AgreementProto) -> Option<Self> {
|
pub fn from_proto(mp: AgreementProto) -> Option<Self> {
|
||||||
if mp.has_bval() {
|
if mp.has_bval() {
|
||||||
Some(AgreementMessage::BVal(mp.get_bval()))
|
Some(AgreementMessage::BVal(mp.get_bval()))
|
||||||
}
|
} else if mp.has_aux() {
|
||||||
else if mp.has_aux() {
|
|
||||||
Some(AgreementMessage::Aux(mp.get_aux()))
|
Some(AgreementMessage::Aux(mp.get_aux()))
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl TestNode {
|
||||||
let (from_id, msg) = self.queue.pop_front().expect("message not found");
|
let (from_id, msg) = self.queue.pop_front().expect("message not found");
|
||||||
debug!("Handling {:?} -> {:?}: {:?}", from_id, self.id, msg);
|
debug!("Handling {:?} -> {:?}: {:?}", from_id, self.id, msg);
|
||||||
let (output, msgs) = self.broadcast
|
let (output, msgs) = self.broadcast
|
||||||
.handle_broadcast_message(&from_id, &msg)
|
.handle_broadcast_message(&from_id, msg)
|
||||||
.expect("handling message");
|
.expect("handling message");
|
||||||
if let Some(output) = output.clone() {
|
if let Some(output) = output.clone() {
|
||||||
self.outputs.push(output);
|
self.outputs.push(output);
|
||||||
|
@ -213,7 +213,6 @@ impl<A: Adversary> TestNetwork<A> {
|
||||||
where
|
where
|
||||||
Q: IntoIterator<Item = TargetedBroadcastMessage<NodeId>> + fmt::Debug,
|
Q: IntoIterator<Item = TargetedBroadcastMessage<NodeId>> + fmt::Debug,
|
||||||
{
|
{
|
||||||
debug!("Sending: {:?}", msgs);
|
|
||||||
for msg in msgs {
|
for msg in msgs {
|
||||||
match msg {
|
match msg {
|
||||||
TargetedBroadcastMessage {
|
TargetedBroadcastMessage {
|
||||||
|
@ -270,12 +269,11 @@ impl<A: Adversary> TestNetwork<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Broadcasts a value from node 0 and expects all good nodes to receive it.
|
/// Broadcasts a value from node 0 and expects all good nodes to receive it.
|
||||||
fn test_broadcast<A: Adversary>(mut network: TestNetwork<A>) {
|
fn test_broadcast<A: Adversary>(mut network: TestNetwork<A>, proposed_value: &[u8]) {
|
||||||
// TODO: This returns an error in all but the first test.
|
// TODO: This returns an error in all but the first test.
|
||||||
let _ = simple_logger::init_with_level(log::Level::Debug);
|
let _ = simple_logger::init_with_level(log::Level::Debug);
|
||||||
|
|
||||||
// Make node 0 propose a value.
|
// Make node 0 propose the value.
|
||||||
let proposed_value = b"Foo";
|
|
||||||
network.propose_value(NodeId(0), proposed_value.to_vec());
|
network.propose_value(NodeId(0), proposed_value.to_vec());
|
||||||
|
|
||||||
// Handle messages in random order until all nodes have output the proposed value.
|
// Handle messages in random order until all nodes have output the proposed value.
|
||||||
|
@ -289,16 +287,34 @@ fn test_broadcast<A: Adversary>(mut network: TestNetwork<A>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Unignore once equal shards don't cause problems anymore.
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_8_broadcast_equal_leaves() {
|
||||||
|
let adversary = SilentAdversary::new(MessageScheduler::Random);
|
||||||
|
// Space is ASCII character 32. So 32 spaces will create shards that are all equal, even if the
|
||||||
|
// length of the value is inserted.
|
||||||
|
test_broadcast(TestNetwork::new(8, 0, adversary), &vec![b' '; 32]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Unignore once node numbers are supported that are not powers of two.
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_13_broadcast_nodes_random_delivery() {
|
||||||
|
let adversary = SilentAdversary::new(MessageScheduler::Random);
|
||||||
|
test_broadcast(TestNetwork::new(13, 0, adversary), b"Foo");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_11_5_broadcast_nodes_random_delivery() {
|
fn test_11_5_broadcast_nodes_random_delivery() {
|
||||||
let adversary = SilentAdversary::new(MessageScheduler::Random);
|
let adversary = SilentAdversary::new(MessageScheduler::Random);
|
||||||
test_broadcast(TestNetwork::new(11, 5, adversary));
|
test_broadcast(TestNetwork::new(11, 5, adversary), b"Foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_11_5_broadcast_nodes_first_delivery() {
|
fn test_11_5_broadcast_nodes_first_delivery() {
|
||||||
let adversary = SilentAdversary::new(MessageScheduler::First);
|
let adversary = SilentAdversary::new(MessageScheduler::First);
|
||||||
test_broadcast(TestNetwork::new(11, 5, adversary));
|
test_broadcast(TestNetwork::new(11, 5, adversary), b"Foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -306,7 +322,7 @@ fn test_11_5_broadcast_nodes_random_delivery_adv_propose() {
|
||||||
let good_nodes: BTreeSet<NodeId> = (0..11).map(NodeId).collect();
|
let good_nodes: BTreeSet<NodeId> = (0..11).map(NodeId).collect();
|
||||||
let adv_nodes: BTreeSet<NodeId> = (11..16).map(NodeId).collect();
|
let adv_nodes: BTreeSet<NodeId> = (11..16).map(NodeId).collect();
|
||||||
let adversary = ProposeAdversary::new(MessageScheduler::Random, good_nodes, adv_nodes);
|
let adversary = ProposeAdversary::new(MessageScheduler::Random, good_nodes, adv_nodes);
|
||||||
test_broadcast(TestNetwork::new(11, 5, adversary));
|
test_broadcast(TestNetwork::new(11, 5, adversary), b"Foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -314,5 +330,5 @@ fn test_11_5_broadcast_nodes_first_delivery_adv_propose() {
|
||||||
let good_nodes: BTreeSet<NodeId> = (0..11).map(NodeId).collect();
|
let good_nodes: BTreeSet<NodeId> = (0..11).map(NodeId).collect();
|
||||||
let adv_nodes: BTreeSet<NodeId> = (11..16).map(NodeId).collect();
|
let adv_nodes: BTreeSet<NodeId> = (11..16).map(NodeId).collect();
|
||||||
let adversary = ProposeAdversary::new(MessageScheduler::First, good_nodes, adv_nodes);
|
let adversary = ProposeAdversary::new(MessageScheduler::First, good_nodes, adv_nodes);
|
||||||
test_broadcast(TestNetwork::new(11, 5, adversary));
|
test_broadcast(TestNetwork::new(11, 5, adversary), b"Foo");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue