-
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
Add DynamicHoneyBadger (part 1). #86
Conversation
src/dynamic_honey_badger.rs
Outdated
self.netinfo = netinfo.clone(); | ||
// TODO: Drop the buffer if this node was removed, to become an observer. | ||
let buffer = self.honey_badger.drain_buffer(); | ||
self.honey_badger = HoneyBadger::new(Rc::new(netinfo), self.batch_size, buffer)?; |
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 you update the batch size here given that the number of nodes has increased?
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.
...or decreased.
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.
src/dynamic_honey_badger.rs
Outdated
Ok(dyn_hb) | ||
} | ||
|
||
fn convert_transaction(&self, input: Input<Tx, NodeUid>) -> Transaction<Tx, NodeUid> { |
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 a better abstraction here would be to implement a conversion trait instead of using a method (because &self
isn't being used):
impl<Tx, NodeUid> From<Input<Tx, NodeUid>> for Transaction<Tx, NodeUid> {
fn from(input: Input<Tx, NodeUid>) -> Self {
match input {
Input::User(tx) => Transaction::User(tx),
Input::Change(change) => Transaction::Change(change),
}
}
}
Then converting from Input -> Transaction
could be written:
let tx = input.into();
or, if the type parameters are necessary:
let tx: Transaction<Self::Input, Self::NodeUid> = input.into();
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're right, I'll change it. (When I originally wrote the method, I still needed self
.)
// TODO: This needs to be the same as `num_faulty` will be in the _new_ | ||
// `NetworkInfo` if the change goes through. It would be safer to deduplicate. | ||
let threshold = (pub_keys.len() - 1) / 3; | ||
let sk = self.netinfo.secret_key().clone(); |
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.
Hmm... this allocates a secret key on stack. I'm not sure what can be done about it though.
I guess we could change self.netinfo.secret_key()
to return a pointer to the heap allocated secret-key, and then change SyncKeyGen.sec_key
to be of type ClearOnDrop<Box<SecretKey>>
.
I'm not sure, but that may be a change that would touch a lot of code, and may be outside the scope of the issue of adding DynamicHoneyBadger
.
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's true. I wonder whether the right thing to do would be to turn what's currently SecretKey
into a private SecretKeyInner
, and put a ClearOnDrop<Box<SecretKeyInner>>
inside the new SecretKey
, so that outside of the crypto
module there's no way to circumvent it anymore?
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 #89 for it.
src/dynamic_honey_badger.rs
Outdated
} | ||
} | ||
*self.vote_counts.entry(change).or_insert(0) += 1; | ||
} |
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.
Would the following code be equivalent to insert_vote
and yet shorter?
if self.votes.insert(id, change.clone()).is_none() {
*self.vote_counts.entry(change).or_insert(0) += 1;
}
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.
Not quite. Sorry for my lousy code comments: The idea is that every node can only have one active vote at a time; voting again revokes the previous vote.
So when we insert a vote, we need to increment the count for that vote, and also decrement the count for the previous vote of that node. Not sure how that can be expressed more concisely.
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.
One way would be to avoid duplication by removing vote_counts
but it does look like a handy optimization. Maybe you could mention this semantics of revoking votes in a comment to Change
so that it is visible?
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.
No, you're right, removing it is better: the optimization is pointless since it's only computed twice per epoch. Also it required more cloning, and if there are a lot of votes it might even be slower, so I removed it.
#[derive(Debug, Clone)] | ||
pub enum Message<NodeUid> { | ||
/// A message belonging to the `HoneyBadger` algorithm started in the given epoch. | ||
HoneyBadger(u64, honey_badger::Message<NodeUid>), |
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 suppose there are going to be other messages in this enum
. Otherwise it would have been a newtype.
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, we will need additional messages so that the joining node can take part in the key generation (DKG): It will have to send its Propose
and Accept
s as messages to the other nodes.
In fact, I think I'll insert this messaging step for all nodes, to prevent censorship. So every node will send its votes and DKG messages first as messages, and everyone who receives a signed message from a peer will propose it in Honey Badger. (Instead of only proposing their own messages, like they do now.)
I think something is still wrong with this (or with the test): Shouldn't the to-be-removed node panic here?. |
DynamicHoneyBadger
is supposed to allow adding and removing nodes. (See #47 (comment)) Internally, it performs Distributed Key Generation and restarts Honey Badger, but all that should be hidden away from the user.This is still incomplete in several ways, but removing a node works.