Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Tendermint fixes #5415

Merged
merged 7 commits into from Apr 10, 2017
Merged

Tendermint fixes #5415

merged 7 commits into from Apr 10, 2017

Conversation

keorn
Copy link

@keorn keorn commented Apr 6, 2017

Fix proposal sync and commit race condition.
Changes the seal format

@keorn keorn added A0-pleasereview 🤓 Pull request needs code review. B7-releasenotes 📜 Changes should be mentioned in the release notes of the next minor version release. M4-core ⛓ Core client code / Rust. labels Apr 6, 2017
@gavofyork
Copy link
Contributor

any chance of a unit test?

@keorn
Copy link
Author

keorn commented Apr 8, 2017

Will try, but it has to do with timing.

Copy link
Collaborator

@tomusdrw tomusdrw left a comment

Choose a reason for hiding this comment

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

Couple of questions that arose from the first glance, I need to go deeper into the logic, cause I don't know the code well enough currently.

@@ -260,8 +241,17 @@ impl Tendermint {
self.validators.contains(&*self.proposal_parent.read(), address)
}

fn is_above_threshold(&self, n: usize) -> bool {
n > self.validators.count(&*self.proposal_parent.read()) * 2/3
fn is_above_threshold(&self, n: usize) -> Result<(), EngineError> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Functions with is_ prefix usually return bool. Maybe check_ or validate_if would be a better name?

fn is_above_threshold(&self, n: usize) -> bool {
n > self.validators.count(&*self.proposal_parent.read()) * 2/3
fn is_above_threshold(&self, n: usize) -> Result<(), EngineError> {
let threshold = self.validators.count(&*self.proposal_parent.read()) * 2/3;
Copy link
Collaborator

Choose a reason for hiding this comment

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

That won't really work for a single validator, right? If you ever reach to such state you will never be able to mint any new blocks.

Copy link
Author

Choose a reason for hiding this comment

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

Integer arithmetic, threshold will be 0, so 1 is enough.

if let Ok(proposal) = ConsensusMessage::new_proposal(header) {
let proposer = proposal.verify()?;
if !self.is_authority(&proposer) {
Err(EngineError::NotAuthorized(proposer))?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think return Err(EngineError::NotAuthorized(proposer)) would be way clearer, but I saw that you're using this pattern in many places already.

Copy link
Author

Choose a reason for hiding this comment

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

You have to do into as well, but can change.

} else {
let vote_step = VoteStep::new(header.number() as usize, consensus_view(header)?, Step::Precommit);
let precommit_hash = message_hash(vote_step.clone(), header.bare_hash());
let ref signatures_field = header.seal()[2];
Copy link
Collaborator

Choose a reason for hiding this comment

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

let signatures_field = &header.seal()[2]; would be enough. Do we ever prove that seal has at least 2 elements? explicit expect would have been better.

Copy link
Author

Choose a reason for hiding this comment

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

In verify_block_basic, sure can do.

let signatures_len = header.seal()[2].len();
if signatures_len >= 1 {
if (proposal_len == 1) ^ (signatures_len == 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't really get that change here.
So the block is valid either if:

  1. there is one proposal and the number of signatures is not one (so 0 or 2 sigs is ok)
  2. or there is one signature and the number of proposals is not one (so 0 or 2 proposals is ok),

Why is that correct? (Shouldn't it be either (a proposal and 0 sigs) or (a signature and 0 proposals)?)

Copy link
Author

Choose a reason for hiding this comment

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

This is sort of wonky, since its just the length of the seal field (1 means no signatures). Will change it to comparing actual RLPs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

!= instead of ^ reads better for bools


if header.number() == 0 {
Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
self.is_above_threshold(signature_count)?
Copy link
Collaborator

Choose a reason for hiding this comment

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

origins.len() would work here, signature_count is redundant.

} else {
Some(Step::Commit)
let bh = message.block_hash.expect("previous guard ensures is_some; qed");
if *self.last_proposed.read() == bh {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are self.step and self.last_proposed always locked in the same order?

Copy link
Author

Choose a reason for hiding this comment

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

They are never write in the same context.

}
self.broadcast_message(rlp.as_raw().to_vec());
if self.votes.vote(message.clone(), &sender).is_some() {
self.validators.report_malicious(&sender);
Err(EngineError::DoubleVote(sender))?
return Err(From::from(EngineError::DoubleVote(sender)));
Copy link
Contributor

Choose a reason for hiding this comment

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

into()?

max: None,
found: signatures_len
})))
warn!(target: "engine", "verify_block_basic: Block is neither a Commit nor Proposal.");
Copy link
Contributor

Choose a reason for hiding this comment

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

this branch is also taken when the seal advertises both, right?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, but currently this is never possible.

@arkpar arkpar added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Apr 10, 2017
@gavofyork gavofyork merged commit d3b2bcd into master Apr 10, 2017
@gavofyork gavofyork deleted the tender-lock branch April 10, 2017 18:03
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. B7-releasenotes 📜 Changes should be mentioned in the release notes of the next minor version release. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants