From dba489ae6bf890a682fda5a76b406edb981783e3 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Fri, 14 Sep 2018 15:47:34 -0400 Subject: [PATCH 01/12] Outputing subset messages as received This outputs subset messages as they are received. All tests pass. --- src/honey_badger/epoch_state.rs | 23 +++++++++++++---- src/subset.rs | 45 +++++++++++++++++++++++++-------- tests/subset.rs | 21 +++++++++------ 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index f5f088a7..4c8edbb1 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -1,5 +1,5 @@ use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::marker::PhantomData; use std::sync::Arc; @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use super::{Batch, ErrorKind, MessageContent, Result, Step}; use fault_log::{Fault, FaultKind, FaultLog}; use messaging::{DistAlgorithm, NetworkInfo}; -use subset::{self as cs, Subset}; +use subset::{self as cs, Subset, SubsetOutput}; use threshold_decryption::{self as td, ThresholdDecryption}; use traits::{Contribution, NodeIdT}; @@ -116,6 +116,8 @@ pub struct EpochState { subset: SubsetState, /// The status of threshold decryption, by proposer. decryption: BTreeMap>, + /// N seen so far + keys: BTreeSet, _phantom: PhantomData, } @@ -132,6 +134,7 @@ where netinfo, subset: SubsetState::Ongoing(cs), decryption: BTreeMap::default(), + keys: Default::default(), _phantom: PhantomData, }) } @@ -219,12 +222,22 @@ where /// Checks whether the subset has output, and if it does, sends out our decryption shares. fn process_subset(&mut self, cs_step: cs::Step) -> Result> { let mut step = Step::default(); - let mut cs_outputs = step.extend_with(cs_step, |cs_msg| { + let mut cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { MessageContent::Subset(cs_msg).with_epoch(self.epoch) }); if let Some(cs_output) = cs_outputs.pop_front() { - self.subset = SubsetState::Complete(cs_output.keys().cloned().collect()); - step.extend(self.send_decryption_shares(cs_output)?); + match cs_output { + SubsetOutput::Contribution(k, v) => { + let mut map: BTreeMap> = Default::default(); + self.keys.insert(k.clone()); + map.insert(k, v); + //step.extend(self.send_decryption_shares(map)?); + } + SubsetOutput::Done(output) => { + self.subset = SubsetState::Complete(output.keys().cloned().collect()); + step.extend(self.send_decryption_shares(output)?); + } + } } if !cs_outputs.is_empty() { error!("Multiple outputs from a single Subset instance."); diff --git a/src/subset.rs b/src/subset.rs index 722cde13..20578fbb 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -89,7 +89,7 @@ pub type Step = messaging::Step>; impl DistAlgorithm for Subset { type NodeId = N; type Input = ProposedValue; - type Output = BTreeMap; + type Output = SubsetOutput; type Message = Message; type Error = Error; @@ -124,6 +124,12 @@ impl DistAlgorithm for Subset { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SubsetOutput { + Contribution(N, Vec), + Done(BTreeMap>), +} + impl Subset { pub fn new(netinfo: Arc>, session_id: u64) -> Result { // Create all broadcast instances. @@ -192,9 +198,11 @@ impl Subset { amessage: binary_agreement::Message, ) -> Result> { // Send the message to the local instance of Binary Agreement. - self.process_binary_agreement(proposer_id, |binary_agreement| { - binary_agreement.handle_message(sender_id, amessage) - }) + self.process_binary_agreement( + proposer_id, + |binary_agreement| binary_agreement.handle_message(sender_id, amessage), + None, + ) } /// Upon delivery of v_j from RBC_j, if input has not yet been provided to @@ -220,7 +228,8 @@ impl Subset { return Ok(step); } }; - self.broadcast_results.insert(proposer_id.clone(), value); + self.broadcast_results + .insert(proposer_id.clone(), value.clone()); let set_binary_agreement_input = |ba: &mut BinaryAgreement| { if ba.accepts_input() { ba.handle_input(true) @@ -228,13 +237,22 @@ impl Subset { Ok(binary_agreement::Step::default()) } }; - step.extend(self.process_binary_agreement(proposer_id, set_binary_agreement_input)?); + step.extend(self.process_binary_agreement( + proposer_id, + set_binary_agreement_input, + Some(value.clone()), + )?); Ok(step) } /// Callback to be invoked on receipt of the decision value of the Binary Agreement /// instance `id`. - fn process_binary_agreement(&mut self, proposer_id: &N, f: F) -> Result> + fn process_binary_agreement( + &mut self, + proposer_id: &N, + f: F, + result: Option>, + ) -> Result> where F: FnOnce(&mut BinaryAgreement) -> binary_agreement::Result>, { @@ -286,7 +304,8 @@ impl Subset { } } } - step.output.extend(self.try_binary_agreement_completion()); + step.output + .extend(self.try_binary_agreement_completion(proposer_id, result)); Ok(step) } @@ -295,7 +314,11 @@ impl Subset { self.ba_results.values().filter(|v| **v).count() } - fn try_binary_agreement_completion(&mut self) -> Option> { + fn try_binary_agreement_completion( + &mut self, + proposer_id: &N, + result: Option>, + ) -> Option> { if self.decided || self.count_true() < self.netinfo.num_correct() { return None; } @@ -303,7 +326,7 @@ impl Subset { // the indexes of each BA that delivered 1. Wait for the output // v_j for each RBC_j such that j∈C. Finally output ∪ j∈C v_j. if self.ba_results.len() < self.netinfo.num_nodes() { - return None; + return Some(SubsetOutput::Contribution(proposer_id.clone(), result?)); } debug!( "{:?} All Binary Agreement instances have terminated", @@ -338,7 +361,7 @@ impl Subset { debug!(" {:?} → {:?}", id, HexBytes(&result)); } self.decided = true; - Some(broadcast_results) + Some(SubsetOutput::Done(broadcast_results)) } else { None } diff --git a/tests/subset.rs b/tests/subset.rs index 92851883..2e5cd04a 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -20,7 +20,7 @@ use std::iter::once; use std::sync::Arc; use hbbft::messaging::NetworkInfo; -use hbbft::subset::Subset; +use hbbft::subset::{Subset, SubsetOutput}; use network::{Adversary, MessageScheduler, NodeId, SilentAdversary, TestNetwork, TestNode}; @@ -54,13 +54,18 @@ fn test_subset>>( } let output = expected.unwrap(); assert!(once(&output).eq(network.observer.outputs())); - // The Subset algorithm guarantees that more than two thirds of the proposed elements - // are in the set. - assert!(output.len() * 3 > inputs.len() * 2); - // Verify that the set's elements match the proposed values. - for (id, value) in output { - assert_eq!(inputs[&id], value); - } + match output { + SubsetOutput::Contribution(_, _) => unreachable!(), + SubsetOutput::Done(output) => { + // The Subset algorithm guarantees that more than two thirds of the proposed elements + // are in the set. + assert!(output.len() * 3 > inputs.len() * 2); + // Verify that the set's elements match the proposed values. + for (id, value) in output { + assert_eq!(inputs[&id], value); + } + } + }; } fn new_network( From ea07bc7d09790bcbeecd7fc1f6243780780420e3 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Sun, 16 Sep 2018 12:05:17 -0400 Subject: [PATCH 02/12] Fix test suite, while still outputing results early This fixes the test suite, while still outputting results early. --- src/honey_badger/epoch_state.rs | 22 ++++++++------- src/subset.rs | 47 ++++++++++++++------------------- tests/subset.rs | 34 +++++++++++------------- 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index 4c8edbb1..b64d9fda 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -117,7 +117,7 @@ pub struct EpochState { /// The status of threshold decryption, by proposer. decryption: BTreeMap>, /// N seen so far - keys: BTreeSet, + map: BTreeMap>, _phantom: PhantomData, } @@ -134,7 +134,7 @@ where netinfo, subset: SubsetState::Ongoing(cs), decryption: BTreeMap::default(), - keys: Default::default(), + map: Default::default(), _phantom: PhantomData, }) } @@ -225,17 +225,21 @@ where let mut cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { MessageContent::Subset(cs_msg).with_epoch(self.epoch) }); - if let Some(cs_output) = cs_outputs.pop_front() { + while let Some(cs_output) = cs_outputs.pop_front() { match cs_output { SubsetOutput::Contribution(k, v) => { - let mut map: BTreeMap> = Default::default(); - self.keys.insert(k.clone()); + self.map.insert(k.clone(), v.clone()); + let mut map = BTreeMap::default(); map.insert(k, v); - //step.extend(self.send_decryption_shares(map)?); } - SubsetOutput::Done(output) => { - self.subset = SubsetState::Complete(output.keys().cloned().collect()); - step.extend(self.send_decryption_shares(output)?); + SubsetOutput::Done(_) => { + //assert_eq!(self.map, output); + + self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); + let q = self.map.clone(); + step.extend(self.send_decryption_shares(q)?); + + break; } } } diff --git a/src/subset.rs b/src/subset.rs index 20578fbb..04ef555e 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -198,11 +198,9 @@ impl Subset { amessage: binary_agreement::Message, ) -> Result> { // Send the message to the local instance of Binary Agreement. - self.process_binary_agreement( - proposer_id, - |binary_agreement| binary_agreement.handle_message(sender_id, amessage), - None, - ) + self.process_binary_agreement(proposer_id, |binary_agreement| { + binary_agreement.handle_message(sender_id, amessage) + }) } /// Upon delivery of v_j from RBC_j, if input has not yet been provided to @@ -237,22 +235,13 @@ impl Subset { Ok(binary_agreement::Step::default()) } }; - step.extend(self.process_binary_agreement( - proposer_id, - set_binary_agreement_input, - Some(value.clone()), - )?); + step.extend(self.process_binary_agreement(proposer_id, set_binary_agreement_input)?); Ok(step) } /// Callback to be invoked on receipt of the decision value of the Binary Agreement /// instance `id`. - fn process_binary_agreement( - &mut self, - proposer_id: &N, - f: F, - result: Option>, - ) -> Result> + fn process_binary_agreement(&mut self, proposer_id: &N, f: F) -> Result> where F: FnOnce(&mut BinaryAgreement) -> binary_agreement::Result>, { @@ -276,15 +265,17 @@ impl Subset { return Ok(step); } }; - if self.ba_results.insert(proposer_id.clone(), value).is_some() { - return Err(Error::MultipleBinaryAgreementResults); - } debug!( "{:?} Updated Binary Agreement results: {:?}", self.netinfo.our_id(), self.ba_results ); + // Binary agreement result accepted. + if self.ba_results.insert(proposer_id.clone(), value).is_some() { + return Err(Error::MultipleBinaryAgreementResults); + } + if value && self.count_true() == self.netinfo.num_correct() { // Upon delivery of value 1 from at least N − f instances of BA, provide // input 0 to each instance of BA that has not yet been provided input. @@ -304,8 +295,14 @@ impl Subset { } } } - step.output - .extend(self.try_binary_agreement_completion(proposer_id, result)); + if let Some(x) = self.broadcast_results.get(proposer_id) { + step.output.extend(Some(SubsetOutput::Contribution( + proposer_id.clone(), + x.clone(), + ))); + } + + step.output.extend(self.try_binary_agreement_completion()); Ok(step) } @@ -314,11 +311,7 @@ impl Subset { self.ba_results.values().filter(|v| **v).count() } - fn try_binary_agreement_completion( - &mut self, - proposer_id: &N, - result: Option>, - ) -> Option> { + fn try_binary_agreement_completion(&mut self) -> Option> { if self.decided || self.count_true() < self.netinfo.num_correct() { return None; } @@ -326,7 +319,7 @@ impl Subset { // the indexes of each BA that delivered 1. Wait for the output // v_j for each RBC_j such that j∈C. Finally output ∪ j∈C v_j. if self.ba_results.len() < self.netinfo.num_nodes() { - return Some(SubsetOutput::Contribution(proposer_id.clone(), result?)); + return None; } debug!( "{:?} All Binary Agreement instances have terminated", diff --git a/tests/subset.rs b/tests/subset.rs index 2e5cd04a..0330b811 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -45,27 +45,25 @@ fn test_subset>>( // Verify that all instances output the same set. let mut expected = None; for node in network.nodes.values() { - if let Some(output) = expected.as_ref() { - assert!(once(output).eq(node.outputs())); - continue; - } - assert_eq!(1, node.outputs().len()); - expected = Some(node.outputs()[0].clone()); + assert!(1 < node.outputs().len()); + expected = Some(node.outputs().clone()); } let output = expected.unwrap(); - assert!(once(&output).eq(network.observer.outputs())); - match output { - SubsetOutput::Contribution(_, _) => unreachable!(), - SubsetOutput::Done(output) => { - // The Subset algorithm guarantees that more than two thirds of the proposed elements - // are in the set. - assert!(output.len() * 3 > inputs.len() * 2); - // Verify that the set's elements match the proposed values. - for (id, value) in output { - assert_eq!(inputs[&id], value); + + // The Subset algorithm guarantees that more than two thirds of the proposed elements + // are in the set. + assert!((output.len() - 1) * 3 > inputs.len() * 2); + for i in output { + match i { + SubsetOutput::Contribution(id, value) => assert_eq!(&inputs[&id], value), + SubsetOutput::Done(output) => { + // Verify that the set's elements match the proposed values. + for (id, value) in output { + assert_eq!(&inputs[&id], value); + } } - } - }; + }; + } } fn new_network( From ec57c14daf4aa585d4799893e6d58c3de3594965 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Sun, 16 Sep 2018 15:03:03 -0400 Subject: [PATCH 03/12] Actually do the optimization There is a testsuite failure in the `dynamic_honey_badger` tests. Is this a testsuite bug? --- src/honey_badger/epoch_state.rs | 4 ++-- src/subset.rs | 20 ++++++++++++-------- tests/subset.rs | 7 +------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index b64d9fda..c6c35601 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -231,13 +231,13 @@ where self.map.insert(k.clone(), v.clone()); let mut map = BTreeMap::default(); map.insert(k, v); + step.extend(self.send_decryption_shares(map)?); } - SubsetOutput::Done(_) => { + SubsetOutput::Done => { //assert_eq!(self.map, output); self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); let q = self.map.clone(); - step.extend(self.send_decryption_shares(q)?); break; } diff --git a/src/subset.rs b/src/subset.rs index 04ef555e..8b48fda7 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -127,7 +127,7 @@ impl DistAlgorithm for Subset { #[derive(Debug, Clone, PartialEq, Eq)] pub enum SubsetOutput { Contribution(N, Vec), - Done(BTreeMap>), + Done, } impl Subset { @@ -227,7 +227,8 @@ impl Subset { } }; self.broadcast_results - .insert(proposer_id.clone(), value.clone()); + .entry(proposer_id.clone()) + .or_insert(value); let set_binary_agreement_input = |ba: &mut BinaryAgreement| { if ba.accepts_input() { ba.handle_input(true) @@ -295,11 +296,14 @@ impl Subset { } } } - if let Some(x) = self.broadcast_results.get(proposer_id) { - step.output.extend(Some(SubsetOutput::Contribution( - proposer_id.clone(), - x.clone(), - ))); + if let Some(x) = self.broadcast_results.insert(proposer_id.clone(), Default::default()) { + let y: Vec = x; + if y.len() > 0 { + step.output.extend(Some(SubsetOutput::Contribution( + proposer_id.clone(), + y, + ))); + } } step.output.extend(self.try_binary_agreement_completion()); @@ -354,7 +358,7 @@ impl Subset { debug!(" {:?} → {:?}", id, HexBytes(&result)); } self.decided = true; - Some(SubsetOutput::Done(broadcast_results)) + Some(SubsetOutput::Done) } else { None } diff --git a/tests/subset.rs b/tests/subset.rs index 0330b811..adbab7af 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -56,12 +56,7 @@ fn test_subset>>( for i in output { match i { SubsetOutput::Contribution(id, value) => assert_eq!(&inputs[&id], value), - SubsetOutput::Done(output) => { - // Verify that the set's elements match the proposed values. - for (id, value) in output { - assert_eq!(&inputs[&id], value); - } - } + SubsetOutput::Done => (), }; } } From bc2597e1b6a6f212151b5fed3dc6885646cca9ee Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Sun, 16 Sep 2018 20:23:48 -0400 Subject: [PATCH 04/12] Respond to code review --- src/honey_badger/epoch_state.rs | 94 +++++++++++++++------------------ src/subset.rs | 73 +++++++++++++------------ tests/subset.rs | 32 +++++++---- 3 files changed, 102 insertions(+), 97 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index c6c35601..78dcc37e 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -222,28 +222,24 @@ where /// Checks whether the subset has output, and if it does, sends out our decryption shares. fn process_subset(&mut self, cs_step: cs::Step) -> Result> { let mut step = Step::default(); - let mut cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { + let cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { MessageContent::Subset(cs_msg).with_epoch(self.epoch) }); - while let Some(cs_output) = cs_outputs.pop_front() { + let expected_num_outputs = cs_outputs.len(); + let mut actual_num_outputs = 0; + for cs_output in cs_outputs { + actual_num_outputs += 1; match cs_output { SubsetOutput::Contribution(k, v) => { - self.map.insert(k.clone(), v.clone()); - let mut map = BTreeMap::default(); - map.insert(k, v); - step.extend(self.send_decryption_shares(map)?); + step.extend(self.send_decryption_share(k.clone(), v.clone())?); } SubsetOutput::Done => { - //assert_eq!(self.map, output); - self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); - let q = self.map.clone(); - break; } } } - if !cs_outputs.is_empty() { + if expected_num_outputs != actual_num_outputs { error!("Multiple outputs from a single Subset instance."); } Ok(step) @@ -267,48 +263,46 @@ where /// Given the output of the Subset algorithm, inputs the ciphertexts into the Threshold /// Decryption instances and sends our own decryption shares. - fn send_decryption_shares(&mut self, cs_output: BTreeMap>) -> Result> { + fn send_decryption_share(&mut self, proposer_id: N, v: Vec) -> Result> { let mut step = Step::default(); - let faulty_shares: Vec<_> = self - .decryption - .keys() - .filter(|id| !cs_output.contains_key(id)) - .cloned() - .collect(); - for id in faulty_shares { - if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) { - for id in td.sender_ids() { - let fault_kind = FaultKind::UnexpectedDecryptionShare; - step.fault_log.append(id.clone(), fault_kind); - } + // let faulty_shares: Vec<_> = self + // .decryption + // .keys() + // .filter(|id| !cs_output.contains_key(id)) + // .cloned() + // .collect(); + // for id in faulty_shares { + // if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) { + // for id in td.sender_ids() { + // let fault_kind = FaultKind::UnexpectedDecryptionShare; + // step.fault_log.append(id.clone(), fault_kind); + // } + // } + // } + let ciphertext: Ciphertext = match bincode::deserialize(&v) { + Ok(ciphertext) => ciphertext, + Err(err) => { + warn!( + "Cannot deserialize ciphertext from {:?}: {:?}", + proposer_id, err + ); + let fault_kind = FaultKind::InvalidCiphertext; + step.fault_log.append(proposer_id, fault_kind); + return Ok(step); } - } - for (proposer_id, v) in cs_output { - let ciphertext: Ciphertext = match bincode::deserialize(&v) { - Ok(ciphertext) => ciphertext, - Err(err) => { - warn!( - "Cannot deserialize ciphertext from {:?}: {:?}", - proposer_id, err - ); - let fault_kind = FaultKind::InvalidCiphertext; - step.fault_log.append(proposer_id, fault_kind); - continue; - } - }; - let td_result = match self.decryption.entry(proposer_id.clone()) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert(DecryptionState::new(self.netinfo.clone())), - }.set_ciphertext(ciphertext); - match td_result { - Ok(td_step) => step.extend(self.process_decryption(proposer_id, td_step)?), - Err(td::Error::InvalidCiphertext(_)) => { - warn!("Invalid ciphertext from {:?}", proposer_id); - let fault_kind = FaultKind::ShareDecryptionFailed; - step.fault_log.append(proposer_id.clone(), fault_kind); - } - Err(err) => return Err(ErrorKind::ThresholdDecryption(err).into()), + }; + let td_result = match self.decryption.entry(proposer_id.clone()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(DecryptionState::new(self.netinfo.clone())), + }.set_ciphertext(ciphertext); + match td_result { + Ok(td_step) => step.extend(self.process_decryption(proposer_id, td_step)?), + Err(td::Error::InvalidCiphertext(_)) => { + warn!("Invalid ciphertext from {:?}", proposer_id); + let fault_kind = FaultKind::ShareDecryptionFailed; + step.fault_log.append(proposer_id.clone(), fault_kind); } + Err(err) => return Err(ErrorKind::ThresholdDecryption(err).into()), } Ok(step) } diff --git a/src/subset.rs b/src/subset.rs index 8b48fda7..a5301060 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -78,7 +78,7 @@ pub struct Subset { netinfo: Arc>, broadcast_instances: BTreeMap>, ba_instances: BTreeMap>, - broadcast_results: BTreeMap, + broadcast_results: BTreeMap>, ba_results: BTreeMap, /// Whether the instance has decided on a value. decided: bool, @@ -228,7 +228,7 @@ impl Subset { }; self.broadcast_results .entry(proposer_id.clone()) - .or_insert(value); + .or_insert(Some(value)); let set_binary_agreement_input = |ba: &mut BinaryAgreement| { if ba.accepts_input() { ba.handle_input(true) @@ -247,7 +247,7 @@ impl Subset { F: FnOnce(&mut BinaryAgreement) -> binary_agreement::Result>, { let mut step = Step::default(); - let value = { + let accepted = { let binary_agreement = self .ba_instances .get_mut(proposer_id) @@ -260,49 +260,51 @@ impl Subset { f(binary_agreement).map_err(Error::ProcessBinaryAgreement0)?, to_msg, ); - if let Some(output) = output.into_iter().next() { - output + if let Some(accepted) = output.into_iter().next() { + accepted } else { return Ok(step); } }; + + // Binary agreement result accepted. + if self + .ba_results + .insert(proposer_id.clone(), accepted) + .is_some() + { + return Err(Error::MultipleBinaryAgreementResults); + } + debug!( "{:?} Updated Binary Agreement results: {:?}", self.netinfo.our_id(), self.ba_results ); - // Binary agreement result accepted. - if self.ba_results.insert(proposer_id.clone(), value).is_some() { - return Err(Error::MultipleBinaryAgreementResults); - } - - if value && self.count_true() == self.netinfo.num_correct() { - // Upon delivery of value 1 from at least N − f instances of BA, provide - // input 0 to each instance of BA that has not yet been provided input. - for (id, binary_agreement) in &mut self.ba_instances { - if binary_agreement.accepts_input() { - let to_msg = |a_msg| Message::BinaryAgreement(id.clone(), a_msg); - for output in step.extend_with( - binary_agreement - .handle_input(false) - .map_err(Error::ProcessBinaryAgreement1)?, - to_msg, - ) { - if self.ba_results.insert(id.clone(), output).is_some() { - return Err(Error::MultipleBinaryAgreementResults); + if accepted { + if self.count_true() == self.netinfo.num_correct() { + // Upon delivery of value 1 from at least N − f instances of BA, provide + // input 0 to each instance of BA that has not yet been provided input. + for (id, binary_agreement) in &mut self.ba_instances { + if binary_agreement.accepts_input() { + let to_msg = |a_msg| Message::BinaryAgreement(id.clone(), a_msg); + for output in step.extend_with( + binary_agreement + .handle_input(false) + .map_err(Error::ProcessBinaryAgreement1)?, + to_msg, + ) { + if self.ba_results.insert(id.clone(), output).is_some() { + return Err(Error::MultipleBinaryAgreementResults); + } } } } } - } - if let Some(x) = self.broadcast_results.insert(proposer_id.clone(), Default::default()) { - let y: Vec = x; - if y.len() > 0 { - step.output.extend(Some(SubsetOutput::Contribution( - proposer_id.clone(), - y, - ))); + if let Some(Some(value)) = self.broadcast_results.insert(proposer_id.clone(), None) { + step.output + .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); } } @@ -342,11 +344,11 @@ impl Subset { ); // Results of Broadcast instances in `delivered_1` - let broadcast_results: BTreeMap = self + let broadcast_results: BTreeSet<&N> = self .broadcast_results .iter() .filter(|(k, _)| delivered_1.contains(k)) - .map(|(k, v)| (k.clone(), v.clone())) + .map(|(k, _)| k) .collect(); if delivered_1.len() == broadcast_results.len() { @@ -354,9 +356,6 @@ impl Subset { "{:?} Binary Agreement instances completed:", self.netinfo.our_id() ); - for (id, result) in &broadcast_results { - debug!(" {:?} → {:?}", id, HexBytes(&result)); - } self.decided = true; Some(SubsetOutput::Done) } else { diff --git a/tests/subset.rs b/tests/subset.rs index adbab7af..111cb644 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -46,19 +46,31 @@ fn test_subset>>( let mut expected = None; for node in network.nodes.values() { assert!(1 < node.outputs().len()); - expected = Some(node.outputs().clone()); - } - let output = expected.unwrap(); + let outputs = node.outputs(); + let (expected_num_outputs, mut actual_num_outputs) = (outputs.len(), 0); + let mut actual = BTreeMap::default(); + + for i in outputs { + match i { + SubsetOutput::Contribution(k, v) => { + actual_num_outputs += 1; + assert!(actual.insert(k, v).is_none()); + } + SubsetOutput::Done => break, + } + } + assert_eq!(expected_num_outputs, actual_num_outputs + 1); - // The Subset algorithm guarantees that more than two thirds of the proposed elements - // are in the set. - assert!((output.len() - 1) * 3 > inputs.len() * 2); - for i in output { - match i { - SubsetOutput::Contribution(id, value) => assert_eq!(&inputs[&id], value), - SubsetOutput::Done => (), + // The Subset algorithm guarantees that more than two thirds of the proposed elements + // are in the set. + assert!(actual_num_outputs * 3 > inputs.len() * 2); + match expected { + None => expected = Some(actual), + Some(ref set) => assert_eq!(&actual, set), }; + } + let output = expected.unwrap(); } fn new_network( From 1b37e14d865e2443857379c7b5d32f237af75a73 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Sun, 16 Sep 2018 20:30:34 -0400 Subject: [PATCH 05/12] Document the meaning of `None` in Subset::broadcast_results --- src/subset.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/subset.rs b/src/subset.rs index a5301060..1a660381 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -78,6 +78,7 @@ pub struct Subset { netinfo: Arc>, broadcast_instances: BTreeMap>, ba_instances: BTreeMap>, + /// `None` means that that item has already been output. broadcast_results: BTreeMap>, ba_results: BTreeMap, /// Whether the instance has decided on a value. From fdf64aec6dd695caddec4f3f55ec10f3f29fc569 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Mon, 17 Sep 2018 10:37:46 -0400 Subject: [PATCH 06/12] Fix adding Contributions and fault check --- src/honey_badger/epoch_state.rs | 48 +++++++++++++-------------------- src/subset.rs | 32 +++++++++++++++++++--- tests/subset.rs | 8 ++---- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index 78dcc37e..8f0f7b28 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -225,23 +225,32 @@ where let cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { MessageContent::Subset(cs_msg).with_epoch(self.epoch) }); - let expected_num_outputs = cs_outputs.len(); - let mut actual_num_outputs = 0; for cs_output in cs_outputs { - actual_num_outputs += 1; match cs_output { SubsetOutput::Contribution(k, v) => { - step.extend(self.send_decryption_share(k.clone(), v.clone())?); + step.extend(self.send_decryption_share(k, v)?); } SubsetOutput::Done => { self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); + + let faulty_shares: Vec<_> = self + .decryption + .keys() + .filter(|id| !self.map.contains_key(id)) + .cloned() + .collect(); + for id in faulty_shares { + if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) { + for id in td.sender_ids() { + let fault_kind = FaultKind::UnexpectedDecryptionShare; + step.fault_log.append(id.clone(), fault_kind); + } + } + } break; } } } - if expected_num_outputs != actual_num_outputs { - error!("Multiple outputs from a single Subset instance."); - } Ok(step) } @@ -264,21 +273,6 @@ where /// Given the output of the Subset algorithm, inputs the ciphertexts into the Threshold /// Decryption instances and sends our own decryption shares. fn send_decryption_share(&mut self, proposer_id: N, v: Vec) -> Result> { - let mut step = Step::default(); - // let faulty_shares: Vec<_> = self - // .decryption - // .keys() - // .filter(|id| !cs_output.contains_key(id)) - // .cloned() - // .collect(); - // for id in faulty_shares { - // if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) { - // for id in td.sender_ids() { - // let fault_kind = FaultKind::UnexpectedDecryptionShare; - // step.fault_log.append(id.clone(), fault_kind); - // } - // } - // } let ciphertext: Ciphertext = match bincode::deserialize(&v) { Ok(ciphertext) => ciphertext, Err(err) => { @@ -286,9 +280,7 @@ where "Cannot deserialize ciphertext from {:?}: {:?}", proposer_id, err ); - let fault_kind = FaultKind::InvalidCiphertext; - step.fault_log.append(proposer_id, fault_kind); - return Ok(step); + return Ok(Fault::new(proposer_id, FaultKind::InvalidCiphertext).into()); } }; let td_result = match self.decryption.entry(proposer_id.clone()) { @@ -296,14 +288,12 @@ where Entry::Vacant(entry) => entry.insert(DecryptionState::new(self.netinfo.clone())), }.set_ciphertext(ciphertext); match td_result { - Ok(td_step) => step.extend(self.process_decryption(proposer_id, td_step)?), + Ok(td_step) => self.process_decryption(proposer_id, td_step), Err(td::Error::InvalidCiphertext(_)) => { warn!("Invalid ciphertext from {:?}", proposer_id); - let fault_kind = FaultKind::ShareDecryptionFailed; - step.fault_log.append(proposer_id.clone(), fault_kind); + Ok(Fault::new(proposer_id.clone(), FaultKind::ShareDecryptionFailed).into()) } Err(err) => return Err(ErrorKind::ThresholdDecryption(err).into()), } - Ok(step) } } diff --git a/src/subset.rs b/src/subset.rs index 1a660381..83b467ee 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -227,9 +227,30 @@ impl Subset { return Ok(step); } }; - self.broadcast_results - .entry(proposer_id.clone()) - .or_insert(Some(value)); + { + let ba_results = &self.ba_results; + let mut did_add_contribution = false; + self.broadcast_results + .entry(proposer_id.clone()) + .or_insert_with(|| { + if let Some(true) = ba_results.get(proposer_id) { + step.output + .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); + did_add_contribution = true; + None + } else { + Some(value) + } + }); + + if did_add_contribution { + for (id, result) in &self.broadcast_results { + if let Some(i) = result { + debug!(" {:?} → {:?}", id, HexBytes(i)); + } + } + } + } let set_binary_agreement_input = |ba: &mut BinaryAgreement| { if ba.accepts_input() { ba.handle_input(true) @@ -306,6 +327,11 @@ impl Subset { if let Some(Some(value)) = self.broadcast_results.insert(proposer_id.clone(), None) { step.output .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); + for (id, result) in &self.broadcast_results { + if let Some(i) = result { + debug!(" {:?} → {:?}", id, HexBytes(i)); + } + } } } diff --git a/tests/subset.rs b/tests/subset.rs index 111cb644..a4493df7 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -45,30 +45,26 @@ fn test_subset>>( // Verify that all instances output the same set. let mut expected = None; for node in network.nodes.values() { - assert!(1 < node.outputs().len()); let outputs = node.outputs(); - let (expected_num_outputs, mut actual_num_outputs) = (outputs.len(), 0); let mut actual = BTreeMap::default(); for i in outputs { match i { SubsetOutput::Contribution(k, v) => { - actual_num_outputs += 1; assert!(actual.insert(k, v).is_none()); } SubsetOutput::Done => break, } } - assert_eq!(expected_num_outputs, actual_num_outputs + 1); + assert_eq!(outputs.len(), actual.len() + 1); // The Subset algorithm guarantees that more than two thirds of the proposed elements // are in the set. assert!(actual_num_outputs * 3 > inputs.len() * 2); match expected { - None => expected = Some(actual), + None => expected = Some(actual), Some(ref set) => assert_eq!(&actual, set), }; - } let output = expected.unwrap(); } From ea4be1ac8ce47b69dee65bb9ea52e436c686e9e2 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Mon, 17 Sep 2018 12:24:18 -0400 Subject: [PATCH 07/12] Fix clippy --- src/honey_badger/epoch_state.rs | 8 ++++---- tests/subset.rs | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index 8f0f7b28..353a5fd8 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -228,7 +228,7 @@ where for cs_output in cs_outputs { match cs_output { SubsetOutput::Contribution(k, v) => { - step.extend(self.send_decryption_share(k, v)?); + step.extend(self.send_decryption_share(k, &v)?); } SubsetOutput::Done => { self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); @@ -272,8 +272,8 @@ where /// Given the output of the Subset algorithm, inputs the ciphertexts into the Threshold /// Decryption instances and sends our own decryption shares. - fn send_decryption_share(&mut self, proposer_id: N, v: Vec) -> Result> { - let ciphertext: Ciphertext = match bincode::deserialize(&v) { + fn send_decryption_share(&mut self, proposer_id: N, v: &[u8]) -> Result> { + let ciphertext: Ciphertext = match bincode::deserialize(v) { Ok(ciphertext) => ciphertext, Err(err) => { warn!( @@ -293,7 +293,7 @@ where warn!("Invalid ciphertext from {:?}", proposer_id); Ok(Fault::new(proposer_id.clone(), FaultKind::ShareDecryptionFailed).into()) } - Err(err) => return Err(ErrorKind::ThresholdDecryption(err).into()), + Err(err) => Err(ErrorKind::ThresholdDecryption(err).into()), } } } diff --git a/tests/subset.rs b/tests/subset.rs index a4493df7..b2b7b509 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -60,13 +60,12 @@ fn test_subset>>( // The Subset algorithm guarantees that more than two thirds of the proposed elements // are in the set. - assert!(actual_num_outputs * 3 > inputs.len() * 2); + assert!(actual.len() * 3 > inputs.len() * 2); match expected { None => expected = Some(actual), Some(ref set) => assert_eq!(&actual, set), }; } - let output = expected.unwrap(); } fn new_network( From fce3cf7997e209a755de964f0a49e03471f9272e Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Tue, 18 Sep 2018 10:02:11 -0400 Subject: [PATCH 08/12] Keep track of nodes that have sent us valid messages Otherwise, we reject all nodes as faulty. --- src/honey_badger/epoch_state.rs | 11 ++++++----- tests/subset.rs | 8 +++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index 353a5fd8..ddff14db 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -117,7 +117,7 @@ pub struct EpochState { /// The status of threshold decryption, by proposer. decryption: BTreeMap>, /// N seen so far - map: BTreeMap>, + nodes_found_so_far: BTreeSet, _phantom: PhantomData, } @@ -134,7 +134,7 @@ where netinfo, subset: SubsetState::Ongoing(cs), decryption: BTreeMap::default(), - map: Default::default(), + nodes_found_so_far: Default::default(), _phantom: PhantomData, }) } @@ -228,15 +228,16 @@ where for cs_output in cs_outputs { match cs_output { SubsetOutput::Contribution(k, v) => { - step.extend(self.send_decryption_share(k, &v)?); + step.extend(self.send_decryption_share(k.clone(), &v)?); + self.nodes_found_so_far.insert(k); } SubsetOutput::Done => { - self.subset = SubsetState::Complete(self.map.keys().cloned().collect()); + self.subset = SubsetState::Complete(self.nodes_found_so_far.clone()); let faulty_shares: Vec<_> = self .decryption .keys() - .filter(|id| !self.map.contains_key(id)) + .filter(|id| !self.nodes_found_so_far.contains(id)) .cloned() .collect(); for id in faulty_shares { diff --git a/tests/subset.rs b/tests/subset.rs index b2b7b509..55f80b63 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -43,7 +43,6 @@ fn test_subset>>( network.step(); } // Verify that all instances output the same set. - let mut expected = None; for node in network.nodes.values() { let outputs = node.outputs(); let mut actual = BTreeMap::default(); @@ -61,10 +60,9 @@ fn test_subset>>( // The Subset algorithm guarantees that more than two thirds of the proposed elements // are in the set. assert!(actual.len() * 3 > inputs.len() * 2); - match expected { - None => expected = Some(actual), - Some(ref set) => assert_eq!(&actual, set), - }; + for (id, value) in actual { + assert_eq!(&inputs[&id], value); + } } } From 5d6a19bd45d790ca1183fa66de0d36829f5753a5 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Tue, 18 Sep 2018 11:38:33 -0400 Subject: [PATCH 09/12] Remove excessive debug logging There is no need to log a quadratic amount of data. --- src/subset.rs | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/subset.rs b/src/subset.rs index 83b467ee..23bfee0a 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -229,26 +229,21 @@ impl Subset { }; { let ba_results = &self.ba_results; - let mut did_add_contribution = false; - self.broadcast_results - .entry(proposer_id.clone()) - .or_insert_with(|| { - if let Some(true) = ba_results.get(proposer_id) { - step.output - .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); - did_add_contribution = true; - None - } else { - Some(value) - } - }); - if did_add_contribution { - for (id, result) in &self.broadcast_results { - if let Some(i) = result { - debug!(" {:?} → {:?}", id, HexBytes(i)); - } - } + let val_to_insert = if let Some(true) = ba_results.get(proposer_id) { + debug!(" {:?} → {:?}", proposer_id, HexBytes(&value)); + step.output + .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); + None + } else { + Some(value) + }; + + if let Some(inval) = self + .broadcast_results + .insert(proposer_id.clone(), val_to_insert) + { + error!("Duplicate insert in broadcast_results: {:?}", inval) } } let set_binary_agreement_input = |ba: &mut BinaryAgreement| { @@ -325,13 +320,9 @@ impl Subset { } } if let Some(Some(value)) = self.broadcast_results.insert(proposer_id.clone(), None) { + debug!(" {:?} → {:?}", proposer_id, HexBytes(&value)); step.output .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); - for (id, result) in &self.broadcast_results { - if let Some(i) = result { - debug!(" {:?} → {:?}", id, HexBytes(i)); - } - } } } From dc7b9f2221717685edb4c86d95801e59f341c468 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Tue, 18 Sep 2018 18:47:14 -0400 Subject: [PATCH 10/12] =?UTF-8?q?Re-add=20check=20that=20the=20observer?= =?UTF-8?q?=E2=80=99s=20values=20match?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the rest of the nodes. Also `panic!` if `Done` is ever not the last value in a series of `SubsetOutput`s. --- src/honey_badger/epoch_state.rs | 8 ++++++-- src/subset.rs | 1 + tests/subset.rs | 22 +++++++++++++++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index ddff14db..33f0418f 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -116,7 +116,7 @@ pub struct EpochState { subset: SubsetState, /// The status of threshold decryption, by proposer. decryption: BTreeMap>, - /// N seen so far + /// Nodes found so far in `Subset` output. nodes_found_so_far: BTreeSet, _phantom: PhantomData, } @@ -225,7 +225,11 @@ where let cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| { MessageContent::Subset(cs_msg).with_epoch(self.epoch) }); + let mut has_seen_done = false; for cs_output in cs_outputs { + if has_seen_done { + panic!("`SubsetOutput::Done` was not the last `SubsetOutput`"); + } match cs_output { SubsetOutput::Contribution(k, v) => { step.extend(self.send_decryption_share(k.clone(), &v)?); @@ -248,7 +252,7 @@ where } } } - break; + has_seen_done = true } } } diff --git a/src/subset.rs b/src/subset.rs index 23bfee0a..d1c530b4 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -131,6 +131,7 @@ pub enum SubsetOutput { Done, } + impl Subset { pub fn new(netinfo: Arc>, session_id: u64) -> Result { // Create all broadcast instances. diff --git a/tests/subset.rs b/tests/subset.rs index 55f80b63..6dfdc030 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -42,17 +42,29 @@ fn test_subset>>( while !network.nodes.values().all(TestNode::terminated) { network.step(); } + + let subsetoutput_to_option = |x: &SubsetOutput<_>| { + match x.clone() { + SubsetOutput::Contribution(x, y) => Some((x, y)), + SubsetOutput::Done => None + } + }; + // Verify that all instances output the same set. + let mut observer: Vec<_> = network.observer.outputs().iter().map(subsetoutput_to_option).collect(); + observer.sort(); for node in network.nodes.values() { - let outputs = node.outputs(); + let mut outputs = node.outputs(); let mut actual = BTreeMap::default(); + let mut has_seen_done = false; for i in outputs { + assert!(!has_seen_done); match i { SubsetOutput::Contribution(k, v) => { assert!(actual.insert(k, v).is_none()); } - SubsetOutput::Done => break, + SubsetOutput::Done => has_seen_done = true, } } assert_eq!(outputs.len(), actual.len() + 1); @@ -61,8 +73,12 @@ fn test_subset>>( // are in the set. assert!(actual.len() * 3 > inputs.len() * 2); for (id, value) in actual { - assert_eq!(&inputs[&id], value); + assert_eq!(&inputs[id], value); } + + let mut outputs: Vec<_> = outputs.iter().map(subsetoutput_to_option).collect(); + outputs.sort(); + assert_eq!(outputs, observer); } } From 06381351f38de14dd86a3e90013643a92fe936a5 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Wed, 19 Sep 2018 15:03:17 -0400 Subject: [PATCH 11/12] Respond to review --- src/honey_badger/epoch_state.rs | 2 +- src/subset.rs | 34 +++++++++++++++------------------ tests/net_dynamic_hb.rs | 6 ++++-- tests/subset.rs | 14 ++------------ 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index 33f0418f..da03a7ab 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -228,7 +228,7 @@ where let mut has_seen_done = false; for cs_output in cs_outputs { if has_seen_done { - panic!("`SubsetOutput::Done` was not the last `SubsetOutput`"); + error!("`SubsetOutput::Done` was not the last `SubsetOutput`"); } match cs_output { SubsetOutput::Contribution(k, v) => { diff --git a/src/subset.rs b/src/subset.rs index d1c530b4..abb84f60 100644 --- a/src/subset.rs +++ b/src/subset.rs @@ -125,13 +125,12 @@ impl DistAlgorithm for Subset { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum SubsetOutput { Contribution(N, Vec), Done, } - impl Subset { pub fn new(netinfo: Arc>, session_id: u64) -> Result { // Create all broadcast instances. @@ -228,24 +227,21 @@ impl Subset { return Ok(step); } }; - { - let ba_results = &self.ba_results; - let val_to_insert = if let Some(true) = ba_results.get(proposer_id) { - debug!(" {:?} → {:?}", proposer_id, HexBytes(&value)); - step.output - .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); - None - } else { - Some(value) - }; - - if let Some(inval) = self - .broadcast_results - .insert(proposer_id.clone(), val_to_insert) - { - error!("Duplicate insert in broadcast_results: {:?}", inval) - } + let val_to_insert = if let Some(true) = self.ba_results.get(proposer_id) { + debug!(" {:?} → {:?}", proposer_id, HexBytes(&value)); + step.output + .extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value))); + None + } else { + Some(value) + }; + + if let Some(inval) = self + .broadcast_results + .insert(proposer_id.clone(), val_to_insert) + { + error!("Duplicate insert in broadcast_results: {:?}", inval) } let set_binary_agreement_input = |ba: &mut BinaryAgreement| { if ba.accepts_input() { diff --git a/tests/net_dynamic_hb.rs b/tests/net_dynamic_hb.rs index ba9b5b17..de554205 100644 --- a/tests/net_dynamic_hb.rs +++ b/tests/net_dynamic_hb.rs @@ -90,10 +90,12 @@ fn do_drop_and_readd(cfg: TestConfig) { // First, we create a new test network with Honey Badger instances. let mut net = NetBuilder::new(0..cfg.dimension.size) .num_faulty(cfg.dimension.faulty) - .message_limit(200_000) // Limited to 200k messages for now. + .message_limit(200_000) // Limited to 200k messages for now. .using_step(move |node| { println!("Constructing new dynamic honey badger node #{}", node.id); - DynamicHoneyBadger::builder().build(node.netinfo).expect("cannot build instance") + DynamicHoneyBadger::builder() + .build(node.netinfo) + .expect("cannot build instance") }).build() .expect("could not construct test network"); diff --git a/tests/subset.rs b/tests/subset.rs index 6dfdc030..00fe717c 100644 --- a/tests/subset.rs +++ b/tests/subset.rs @@ -43,16 +43,8 @@ fn test_subset>>( network.step(); } - let subsetoutput_to_option = |x: &SubsetOutput<_>| { - match x.clone() { - SubsetOutput::Contribution(x, y) => Some((x, y)), - SubsetOutput::Done => None - } - }; - // Verify that all instances output the same set. - let mut observer: Vec<_> = network.observer.outputs().iter().map(subsetoutput_to_option).collect(); - observer.sort(); + let observer: BTreeSet<_> = network.observer.outputs().iter().cloned().collect(); for node in network.nodes.values() { let mut outputs = node.outputs(); let mut actual = BTreeMap::default(); @@ -76,9 +68,7 @@ fn test_subset>>( assert_eq!(&inputs[id], value); } - let mut outputs: Vec<_> = outputs.iter().map(subsetoutput_to_option).collect(); - outputs.sort(); - assert_eq!(outputs, observer); + assert_eq!(outputs.iter().cloned().collect::>(), observer); } } From 74e44deacd0d19709d7d745dee27d553438aeef5 Mon Sep 17 00:00:00 2001 From: "Demi M. Obenour" Date: Wed, 19 Sep 2018 15:31:39 -0400 Subject: [PATCH 12/12] Rename field --- src/honey_badger/epoch_state.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index da03a7ab..9092a562 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -117,7 +117,7 @@ pub struct EpochState { /// The status of threshold decryption, by proposer. decryption: BTreeMap>, /// Nodes found so far in `Subset` output. - nodes_found_so_far: BTreeSet, + accepted_proposers: BTreeSet, _phantom: PhantomData, } @@ -134,7 +134,7 @@ where netinfo, subset: SubsetState::Ongoing(cs), decryption: BTreeMap::default(), - nodes_found_so_far: Default::default(), + accepted_proposers: Default::default(), _phantom: PhantomData, }) } @@ -233,15 +233,15 @@ where match cs_output { SubsetOutput::Contribution(k, v) => { step.extend(self.send_decryption_share(k.clone(), &v)?); - self.nodes_found_so_far.insert(k); + self.accepted_proposers.insert(k); } SubsetOutput::Done => { - self.subset = SubsetState::Complete(self.nodes_found_so_far.clone()); + self.subset = SubsetState::Complete(self.accepted_proposers.clone()); let faulty_shares: Vec<_> = self .decryption .keys() - .filter(|id| !self.nodes_found_so_far.contains(id)) + .filter(|id| !self.accepted_proposers.contains(id)) .cloned() .collect(); for id in faulty_shares {