-
Couldn't load subscription status.
- Fork 1
Request ancestor blocks for previous rounds #264
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
Conversation
b751a1f to
0571fa8
Compare
401e2f3 to
3374993
Compare
This commit makes the node request past ancestor blocks and notarizations it doesn't have. It is needed in case the node notarizes one or more empty blocks while the rest of the nodes notarize blocks in these rounds. Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
3374993 to
8cdf1c6
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.
I'm concerned this is a lot of code changes and additional complexity for a corner case. All we are trying to do is resend the notarization if we receive a block with a parent we don't know. Why can't our current logic with timeouts and replication solve this? When our node eventually times out on the round, it will send an empty vote. If that vote is for an old round, nodes will respond with their most recent round/seq as well as the round/seq for the stale empty vote.
Secondly and I think most importantly, if a node empty notarizes a round then it shouldn't be swayed and by notarizations right? Only a finalization should be able to change that nodes mind otherwise we can get a quorum of nodes that sign off on both an empty notarization & finalization for the same round.
| recordedMessages := make(chan *Message, 100) | ||
| comm := &recordingComm{Communication: testutil.NoopComm(nodes), SentMessages: recordedMessages, BroadcastMessages: recordedMessages} | ||
|
|
||
| bb2 := &testutil.TestBlockBuilder{Out: make(chan *testutil.TestBlock, 1), BlockShouldBeBuilt: make(chan struct{}, 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.
why create a second BB?
| t.Log("Last block is", lastBlock.BlockHeader().Seq, "for round", lastBlock.BlockHeader().Round) | ||
|
|
||
| leaderIndexOfLastBlock := (int(lastBlock.BlockHeader().Round)) % len(nodes) | ||
| voteOnLastBlock, err := testutil.NewTestVote(lastBlock, nodes[leaderIndexOfLastBlock]) |
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 err value never gets checked
| return nil, nil | ||
| } | ||
| seq := msg.NotarizedBlockRequest.Seq | ||
| for i, b := range blocks { |
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.
ik this is a test so it doesn't really matter as much, but would it be easier to store blocks as a map indexed by their seqs?
|
|
||
| type NotarizedBlockResponse struct { | ||
| Block Block | ||
| VerifiedBlock VerifiedBlock |
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 we should separate these into NotarizedBlockResponse and VerifiedNotarizedBlockResponse just like the other messages
| e.increaseRound() | ||
| increasedRound = true | ||
| } | ||
| if increasedRound { |
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 this important for this pr, or is it a separate bug fix?
| func (e *Epoch) maybeCreateFinalizeVoteForAncestor(digest Digest) Digest { | ||
| for roundNum, round := range e.rounds { | ||
| if round.block.BlockHeader().Digest != digest { | ||
| continue |
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.
if we are checking for this, should we log a warn? every round in the rounds map should always have a block
| zap.Int("size", len(recordBytes)), | ||
| zap.Stringer("digest", finalization.Finalization.BlockHeader.Digest)) | ||
|
|
||
| e.finalizeAncestors(finalization.Finalization.Prev) |
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 we add a short comment to why this is important, i feel like I am going to forget down the line
| vote := message.Vote | ||
| from = vote.Signature.Signer | ||
|
|
||
| e.Logger.Debug("Handling block message", zap.Stringer("digest", md.Digest), zap.Uint64("round", md.Round)) |
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.
why is this log being removed?
| return e.Storage.NumBlocks() | ||
| } | ||
|
|
||
| func (e *Epoch) haveWeTimedOutOnRound(round uint64) bool { |
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 have a method with a very similar name already. haveWeAlreadyTimedOutOnThisRound checks for timedOut while this checks for an emptyNotarization
| return nil | ||
| } | ||
|
|
||
| if response.Block != nil { |
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 will always be true since we check the negation above
Because here we replicate rounds in the past, and the replication logic replicates rounds from the future.
Here the assumption is that all nodes are in the latest round. This doesn't apply for nodes that are behind.
If a node notarizes an empty round, it will not send a finalize vote for that round. However, it should still be receptive to blocks built on a valid alternative chain. There is no problem replicating notarizations as long as we remember not sending finalize votes for them. Otherwise, a single node that missed one or more blocks may force the rest of the nodes to notarize empty rounds until it is the leader again. Consider we have 4 nodes and one (node 1) has built and broadcast a block. The rest of the nodes (0, 1, 3) notarize an empty round for that round. A bigger problem is if node 3 crashes in this round - then we will notarize an empty round for the block of node 2 and also for the block of node 3.
I tend to agree. If we're OK with unwanted and sub-optimal latency in case of a network failure then we can just agree to not address this corner case. |
This commit makes the node request past ancestor blocks and notarizations it doesn't have.
It is needed in case the node notarizes one or more empty blocks while the rest of the nodes notarize blocks in these rounds.