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

Introduce EventValidBlock for informing peers about wanted block #2652

Merged
merged 5 commits into from Oct 31, 2018

Conversation

milosevic
Copy link
Contributor

@milosevic milosevic commented Oct 17, 2018

Fix #2583. Note that tests that check consensus reactor part will be addressed in the follow up PR.

  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Updated CHANGELOG_PENDING.md

@milosevic milosevic force-pushed the zarko/2583-enable-valid-block-property branch from 66b0938 to a06929b Compare October 19, 2018 08:56
@codecov-io
Copy link

codecov-io commented Oct 19, 2018

Codecov Report

Merging #2652 into develop will increase coverage by 0.19%.
The diff coverage is 88.13%.

@@            Coverage Diff             @@
##           develop   #2652      +/-   ##
==========================================
+ Coverage    61.61%   61.8%   +0.19%     
==========================================
  Files          207     207              
  Lines        16942   16952      +10     
==========================================
+ Hits         10439   10478      +39     
+ Misses        5640    5615      -25     
+ Partials       863     859       -4
Impacted Files Coverage Δ
consensus/state.go 80.02% <100%> (+3.84%) ⬆️
p2p/metrics.go 17.94% <100%> (ø) ⬆️
consensus/reactor.go 72.34% <80%> (+0.34%) ⬆️
blockchain/pool.go 65.73% <0%> (-0.7%) ⬇️
p2p/pex/pex_reactor.go 74% <0%> (-0.34%) ⬇️
p2p/conn/connection.go 79.43% <0%> (-0.29%) ⬇️

@milosevic milosevic force-pushed the zarko/2583-enable-valid-block-property branch 5 times, most recently from 3111d10 to a4cde65 Compare October 19, 2018 09:37
@milosevic milosevic force-pushed the zarko/2583-enable-valid-block-property branch from a4cde65 to 72637c5 Compare October 19, 2018 09:50
@milosevic milosevic force-pushed the zarko/2583-enable-valid-block-property branch from 72637c5 to 8ec9a55 Compare October 19, 2018 09:54
@milosevic milosevic changed the title [WIP] Introduce EventValidBlock for informing peers about wanted block Introduce EventValidBlock for informing peers about wanted block Oct 19, 2018
@@ -392,13 +397,18 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(hb *types.Heartb
}

func (conR *ConsensusReactor) broadcastNewRoundStepMessages(rs *cstypes.RoundState) {
Copy link
Contributor

Choose a reason for hiding this comment

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

broadcastNewRoundStepMessage

"Valid block we don't know about. Set ProposalBlock=nil",
"proposal", cs.ProposalBlock.Hash(), "blockId", blockID.Hash)
// We're getting the wrong block.
cs.ProposalBlock = nil
Copy link
Contributor

Choose a reason for hiding this comment

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

what about cs.ValidRound and cs.ValidBlockParts? Shouldn't we reset these too?

Copy link
Contributor Author

@milosevic milosevic Oct 25, 2018

Choose a reason for hiding this comment

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

We update cs.ValidRound, cs.ValidBlockParts and cs.ValidBlock only when we have the corresponding block. Therefore, at this point we can only "tell" gossip layer that we need some block, and then update ValidX variables when block arrives (if it arrives on time).

Copy link
Contributor

@ebuchman ebuchman left a comment

Choose a reason for hiding this comment

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

Great work, but still have a few questions and some minor nits.

@@ -964,7 +963,8 @@ func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round {
return
}
if ps.PRS.Proposal {
// ps.PRS.ProposalBlockParts is set due to NewValidBlockMessage
if ps.PRS.Proposal || ps.PRS.ProposalBlockParts != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really necessary?

I believe the situation this captures is when they don't have a proposal, but they sent us NewValidBlockMessage. For this to run, then either they'd have to send us a proposal (which I think would be Byzantine) or we'd have to have tried to send it to them (should that ever happen?).

I think it does make sense to have the check here, just hard to follow the circumstance where !PRS.Proposal && PRS.ProposalBlockParts != nil

Copy link
Contributor Author

@milosevic milosevic Oct 25, 2018

Choose a reason for hiding this comment

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

The key part here is ps.PRS.ProposalBlockParts != nil. In this case we should't update PRS.ProposalBlockPartsHeader as it is already set (either by previous Proposal message or NewValidBlockMessage). But we might still want to set PRS.Proposal to true in case it is not already set. So maybe we should split this into 2 conditions. Regarding your last point, negation is !PRS.Proposal && PRS.ProposalBlockParts == nil and this corresponds to the case that we are receiving Proposal for the first time and we haven't received NewValidBlockMessage yet.

ps.mtx.Lock()
defer ps.mtx.Unlock()

if ps.PRS.Height != msg.Height {
if ps.PRS.Height != msg.Height && ps.PRS.Round != msg.Round {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if there was a commit in Round 2, but this peer never received the block from Round 2, and is already in Round 3. Then it sees the commit from round 2, and sends us NewValidBlockMessage with Round==2?

Do we need special logic for when the NewValidBlockMessage is specifically for a commit, since the round may be different then?

Hard to write a test for this right now :(

Copy link
Contributor Author

@milosevic milosevic Oct 25, 2018

Choose a reason for hiding this comment

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

Excellent point, I missed this. We probably need to treat commit in a special way. Maybe we can just add flag to NewValidBlockMessage to say if it is commit or not. And yes, we don't have tests for this, but it will come soon :)

@@ -1420,11 +1415,6 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
return nil
}

// We don't care about the proposal if we're already in cstypes.RoundStepCommit.
Copy link
Contributor

Choose a reason for hiding this comment

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

This line was pointed out as the reason #2567 wasn't a bug (ie. see #2567 (comment)).

Now, instead of checking the commit step, we check if the ProposalBlockParts is already set, so we don't overwrite it. Is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was aware of #2567 and was convinced that we still have the same guarantee with the new condition. I will make one more check.

@@ -1616,16 +1611,26 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,

// Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka..
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we consider vote.Round < cs.Round anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cs.ProposalBlock corresponds to the cs.Round. The spec also only cares about updating validBlock for the current round.

ensurePrecommit(voteCh, height, round)
// we should have precommitted
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)

Copy link
Contributor

Choose a reason for hiding this comment

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

We should add checks here to show that ValidBlock and ValidBlockParts are currently empty

t.Fatal(err)
}

time.Sleep(20 * time.Millisecond)
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh this isn't good. Can't we wait on the CompleteProposal event here instead?

Copy link
Contributor Author

@milosevic milosevic Oct 25, 2018

Choose a reason for hiding this comment

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

Good point. The only point at which we emit CompleteProposal is in enterPrevote (

cs.eventBus.PublishEventCompleteProposal(cs.RoundStateEvent())
), so it does not work out of the box. But I will modify this to emit CompleteProposal upon receiving CompleteProposal in addProposalBlockPart, and then it's fine. We should probably add more tests to check when we are emitting events as they are crucial for overall functioning of the system.

startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)

// vs2, vs3 and vs4 send prevote for propBlock
Copy link
Contributor

Choose a reason for hiding this comment

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

send precommit

assert.True(t, rs.Step == cstypes.RoundStepCommit)
assert.True(t, rs.ProposalBlock == nil)
assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header()))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Awesome.

Can we add another test for the situation where we've already gone to the next round, but then see the commit for the previous round, ie. what was described in #2567 ?

Also, out of curiosity, what happens if we propose a block and then see +2/3 precommit for a different block in that round? That's supposed to imply +2/3 are Byzantine, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the current version of the code this can happen as we are prevoting for the locked value, so it can be normal case. I prefer more having prevote to reflect reaction to proposal (or absence of proposal) like done in spec; in that case seeing +2/3 precommit for a different block means we have +2/3 Byzantine as correct can only precommit for a proposed value in case proposer is correct, or 2) private key of current proposer is stolen :)

consensus/reactor.go Show resolved Hide resolved
@ebuchman
Copy link
Contributor

ebuchman commented Oct 25, 2018

If we release v0.26.0 before merging this, we should consider trying to do this in a backwards compatible manner - ie. keep the CommitStepMessage, and only send the NewValidBlock message to peers on the right P2PVersion. We'll have to make the peer's P2PVersion available somehow to the reactor.

Zarko Milosevic added 2 commits October 29, 2018 17:44
- Add test for the case of +2/3 Precommit from the previous round
@milosevic milosevic mentioned this pull request Oct 31, 2018
4 tasks
@ebuchman ebuchman merged commit 7a03344 into develop Oct 31, 2018
@ebuchman ebuchman deleted the zarko/2583-enable-valid-block-property branch June 24, 2019 14:37
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.

None yet

4 participants