Skip to content
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

Merged
merged 4 commits into from
Jun 27, 2018
Merged

Add DynamicHoneyBadger (part 1). #86

merged 4 commits into from
Jun 27, 2018

Conversation

afck
Copy link
Collaborator

@afck afck commented Jun 26, 2018

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.

@afck afck changed the title Add DynamicHoneyBadger (still incomplete). Add DynamicHoneyBadger (part 1). Jun 26, 2018
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)?;
Copy link
Contributor

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...or decreased.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the exact requirements; there is still a TODO in the code for that. I created issue #88 for it.

For now, the code does the right thing in the situation of the issue's first bullet point, i.e. where we want a constant block size.

Ok(dyn_hb)
}

fn convert_transaction(&self, input: Input<Tx, NodeUid>) -> Transaction<Tx, NodeUid> {
Copy link
Contributor

@DrPeterVanNostrand DrPeterVanNostrand Jun 26, 2018

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();

Copy link
Collaborator Author

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();
Copy link
Contributor

@DrPeterVanNostrand DrPeterVanNostrand Jun 26, 2018

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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.

}
}
*self.vote_counts.entry(change).or_insert(0) += 1;
}
Copy link
Contributor

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;
}

Copy link
Collaborator Author

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.

Copy link
Contributor

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?

Copy link
Collaborator Author

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>),
Copy link
Contributor

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.

Copy link
Collaborator Author

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 Accepts 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.)

@afck
Copy link
Collaborator Author

afck commented Jun 26, 2018

I think something is still wrong with this (or with the test): Shouldn't the to-be-removed node panic here?.
Ah, I found it! I'll push a fix soon…

@vkomenda vkomenda merged commit f7dd834 into master Jun 27, 2018
@afck afck deleted the afck-dyn-hb branch June 27, 2018 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants