-
Notifications
You must be signed in to change notification settings - Fork 96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Encryption of proposals in HoneyBadger #68
Conversation
src/honey_badger.rs
Outdated
self.handle_common_subset_message(sender_id, epoch, cs_msg) | ||
} | ||
MessageContent::DecryptionShare { proposer_id, share } => { | ||
self.handle_decryption_share_message(sender_id, epoch, &proposer_id, &share) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could pass the proposer ID and share by value to avoid the cloning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then clippy
starts complaining on "needless" pass-by-value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But not if you remove the .clone()
in handle_decryption_share_message
.
src/honey_badger.rs
Outdated
.collect(), | ||
)].into_iter() | ||
.collect() | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is another point where we'll need to think about spam protection (#43) in the future, i.e. guard against a node sending us tons of fake decryption shares for later epochs. But I don't have an idea how to do that yet.
src/honey_badger.rs
Outdated
.decrypted_selections | ||
.iter() | ||
.flat_map(|(proposer_id, ser_batch)| { | ||
// If serialization fails, the proposer of that batch is faulty. Ignore it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deserialization
?
src/honey_badger.rs
Outdated
let mut proposer_ids: Option<BTreeSet<NodeUid>> = None; | ||
if let Some(shares) = self.received_shares.get(&self.epoch) { | ||
proposer_ids = Some(shares.keys().cloned().collect()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With else { return Ok(false); }
, the proposer_ids
wouldn't have to be an option and could be immutable.
src/honey_badger.rs
Outdated
} | ||
|
||
/// Tries to decrypt the selection of transactions from a given proposer. Outputs `true` if and | ||
/// only of decryption finished without errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
of
→ if
self.try_output_batch()?; | ||
} | ||
|
||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to pk.verify_decryption_share(&share, &ciphertext)
with the public key share pk
of the sender as soon as we have both the ciphertext and the share. Then we drop all invalid shares, so we can be sure that any set of f + 1
share will successfully yield the proposer's input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created a separate issue to test my proposed implementation: #75. I suggest adding that test after we finish working on this PR.
src/honey_badger.rs
Outdated
.collect(), | ||
)].into_iter() | ||
.collect() | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be simplified:
self.received_shares
.entry(epoch)
.or_insert_with(BTreeSet::new)
.entry(proposer_id.clone())
.or_insert_with(BTreeSet::new)
.insert(sender_id.clone(), share.clone());
src/honey_badger.rs
Outdated
// If serialization fails, the proposer of that batch is faulty. Ignore it. | ||
bincode::deserialize::<Vec<Tx>>(&ser_batch) | ||
.ok() | ||
.map(|proposed| (proposer_id.clone(), proposed)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something to keep in mind for #51: We could add another field (faulty_nodes
?) to Batch
, to report the nodes whose input didn't deserialize.
src/honey_badger.rs
Outdated
if !proposer_ids.is_empty() | ||
&& proposer_ids | ||
.iter() | ||
.all(|proposer_id| self.try_decrypt_proposer_selection(proposer_id)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The criteria here should probably be:
CommonSubset
has already output in this epoch.- For every proposer from
CommonSubset
's output, we have receivedf + 1
valid decryption shares.
If there are at most f
faulty nodes, it should be guaranteed that that eventually happens and that decryption succeeds.
Calling try_decrypt_proposer_selection
every time looks like it might be expensive: wouldn't that repeatedly decrypt and overwrite the same thing? (Or should that method just return true
immediately, if there's already an entry in decrypted_selections
?)
src/honey_badger.rs
Outdated
let all_ciphertext_proposers: BTreeSet<_> = ciphertexts.keys().collect(); | ||
let all_decrypted_selection_proposers: BTreeSet<_> = | ||
self.decrypted_selections.keys().collect(); | ||
all_ciphertext_proposers.is_subset(&all_decrypted_selection_proposers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a proper subset or would it just be equal?
dba94e6
to
8d0180b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
Just one possible simplification (but maybe I'm just confused by all those nested maps).
src/honey_badger.rs
Outdated
@@ -60,6 +60,9 @@ pub struct HoneyBadger<Tx, NodeUid> { | |||
decrypted_selections: BTreeMap<NodeUid, Vec<u8>>, | |||
/// Ciphertexts output by Common Subset in an epoch. | |||
ciphertexts: BTreeMap<u64, BTreeMap<NodeUid, Ciphertext<Bls12>>>, | |||
/// Those shares in `received_shares` that could not be verified on reception due to the absense |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is keeping track about those even necessary? When we receive the ciphertext, doesn't that mean that none of the existing shares have been verified yet? I.e. won't this set always either be equal to the senders whose shares we've already received (if we don't have the ciphertext yet), or empty (if we have it)?
(Also typo: absence
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is indeed the case. Thanks for spotting this.
src/honey_badger.rs
Outdated
@@ -104,7 +104,10 @@ where | |||
} | |||
|
|||
fn next_message(&mut self) -> Option<TargetedMessage<Self::Message, NodeUid>> { | |||
self.messages.pop_front() | |||
let convert = |msg: TargetedMessage<InternalMessage<NodeUid>, NodeUid>| { | |||
msg.map(|int_msg| int_msg.into_external()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That probably doesn't need a closure: map(InternalMessage::into_external)
.
src/honey_badger.rs
Outdated
InternalMessageContent::CommonSubset(msg) => MessageContent::CommonSubset(msg), | ||
InternalMessageContent::DecryptionShare { proposer_id, share } => { | ||
// FIXME: Handle errors. | ||
let share_copy = Rc::try_unwrap(share).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that will usually fail: Most of the time, the local reference will live longer than the message.
I hadn't realized that Rc
doesn't automatically implement the serde traits. Maybe instead of the internal-external distinction the message should just always have an Rc
, but we should implement Serialize
and Deserialize
manually? (Or rather with some attribute #[serde(with = "a_new_rc_serde_module")]
.)
But maybe this is all premature optimization; I'm happy to postpone that to a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, Rc::try_unwrap()
does fail here. I'll try to experiment with custom Serialize
and Deserialize
. There seems to be no shortcut to that and I think you are right to say it may deserve a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue #77
…s shares in messages" This reverts commit 10c442a.
0feb6fa
to
4c2e92e
Compare
Close #41