Conversation
6be0d9c to
23d9c67
Compare
9c76fa7 to
9501164
Compare
9501164 to
a1bb893
Compare
9d1886a to
b4bfaba
Compare
b4bfaba to
4b6b2f5
Compare
a1a3fb9 to
b507b64
Compare
|
Looks like this test demonstrates we have a liveness problem in case we miss the first broadcast we send via |
|
In the issue #530 I wrote:
The below test demonstrates that that's not what we do: |
| return nil | ||
| } | ||
|
|
||
| n.Logger.Info("Bootstrapped, received a threshold of sealing block info for an epoch", zap.Stringer("Info", qr.Block.SealingBlockInfo())) |
There was a problem hiding this comment.
We should only consider ourselves as bootstrapped when we have replicated and committed all blocks from the last block in the ledger to the last known tip.
There was a problem hiding this comment.
i think we can consider ourselves bootstrapped once we have validated the hash chain of sealing blocks, then we can start as normal syncing all the blocks in between
There was a problem hiding this comment.
as to your test, i made a few non-validator tests to ensure we do the backwards hash validation first
TestNonValidator_BootstrapIgnoresSealingBlockOffChain && TestNonValidator_BootstrapWalksHashChain
b04bddb to
c2d3997
Compare
yacovm
left a comment
There was a problem hiding this comment.
These are the comments I have so far, I'm not nearly done with the review.
| // and it is in the validator set | ||
| TransitionToValidator func(epoch uint64, validators common.Nodes) | ||
|
|
||
| // Bootstrapped is set once every epoch from our tip up to the one a threshold of the latest |
There was a problem hiding this comment.
Why would we call that bootstrapped? Bootstrapped should just mean that we finished bootstrapping, exactly like we do in snowman. Which is that we have replicated all blocks we know are missing from the latest discovered tip down to the tip before bootstrapping.
There was a problem hiding this comment.
updated from bootstrapped terminology to EpochsReplicated 57c282e
| return nil | ||
| } | ||
|
|
||
| // No sealing block is missing, so every epoch from our tip to the highest is validated. |
There was a problem hiding this comment.
but we should still replicate all blocks in the last epoch that we know about before we declare that we have finished bootstrapping.
There was a problem hiding this comment.
I think this comment is still relevant
There was a problem hiding this comment.
updated from bootstrapped terminology to EpochsReplicated 57c282e
| } | ||
|
|
||
| comm := newCommunication(i.Config.Sender, i.Config.Broadcaster, mappings.Nodes()) | ||
| comm := newCommunication(i.Config.Sender, i.Config.Broadcaster, latestValidatorSet.Nodes()) |
There was a problem hiding this comment.
unrelated to this PR, but... what updates the comm's validator set once we move through epochs after we bootstrap?
There was a problem hiding this comment.
yea we need to change the non-validator comm to not hardcode its Validators() method. everytime it calls Validators the pchain should be queried or something
d7defc8 to
8fa6844
Compare
2a76d39 to
a02c097
Compare
| } | ||
| nv.sealingBlockTimeouts = common.NewTimeoutHandler(config.Logger, "sealing block replication", config.StartTime, simplex.DefaultReplicationRequestTimeout, nv.requestMissingSealingBlocks) | ||
| if !config.Bootstrapped { | ||
| nv.sealingBlockTimeouts.AddTask(startBroadcastTask) |
There was a problem hiding this comment.
nv.sealingBlockTimeouts.AddTask(0) - I don't understand what we're trying to do here.
There was a problem hiding this comment.
i updated it to
// initialBootstrapTask is the sealingBlockTimeouts task that continuously asks for sealing blocks until a
// threshold of responses validates an epoch. We use Seq 1, since requests with Seq 0 are dropped.
const initialBootstrapTask uint64 = 1This timeout task only gets removed when we validate an epoch, otherwise it will keep sending replication requests. This avoids the problem where we send a request, but that request gets dropped and never delivered.
I also moved adding the task to Start.
| // The finalization has not been verified yet. Storing tells the replicator a valid sequence exists | ||
| // and its validity is checked when the round is processed. | ||
| func (n *NonValidator) validateSealingBlock(qr *common.QuorumRound, from common.NodeID) { | ||
| n.maybeValidateNextEpoch(qr.Block, from) |
There was a problem hiding this comment.
It looks like we maybe validate and then anyway store the QR?
but maybeValidateNextEpoch can return early in many cases. Is that intentional?
I guess the purpose of validateSealingBlock is to kickstart validation of the previous sealing block? If so, is this the right name for the method?
There was a problem hiding this comment.
yea we only call validateSealingBlock when the sealing block can be validated. so we should store the quorum round.
also maybeValidateNextEpoch is called in case the block is sealing block, not necessarily from replication.
What would u suggest the name be?
There was a problem hiding this comment.
Not really relevant to this PR, but - if validators changes between two invocations of this function, then we have a problem.
Consider two invocations collectedSealingBlockInfo() in t1 and collectedSealingBlockInfo() in t2, and the first one sampled validators = [v1, v2, v3, v4] and the second once sampled v2, v3, v4, v5]'.
The first one got a vote from v1 and the second one got a vote from v2 but v1 is not in the second.
We reached the threshold f+1 but with an illegal count.
| return nil | ||
| } | ||
|
|
||
| // No sealing block is missing, so every epoch from our tip to the highest is validated. |
There was a problem hiding this comment.
I think this comment is still relevant
a02c097 to
32715c8
Compare
Addresses #530 but for non-validator and validators.
Nodes will first need to complete bootstrapping before being able to index/verify blocks.