-
Notifications
You must be signed in to change notification settings - Fork 28
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 VRF-based Proposer Election #55
Conversation
14e2321
to
59ad1b1
Compare
127f830
to
dbaaa2d
Compare
dbaaa2d
to
01ca490
Compare
state/state.go
Outdated
hash := tmhash.New() | ||
hash.Write(state.LastProofHash) | ||
return hash.Sum(b), nil | ||
return MakeRoundHash(state.LastProofHash, state.LastBlockHeight, round), 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.
How about removing error
return?
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.
Yes, I'll remove the error
as it doesn't make sense.
state/state.go
Outdated
validatorSet = types.NewValidatorSet(validators) | ||
nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) | ||
validatorSet = types.NewRandomValidatorSet(validators, MakeRoundHash(genDoc.Hash(), 0, 0)) | ||
nextValidatorSet = types.NewRandomValidatorSet(validators, MakeRoundHash(genDoc.Hash(), 0, 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.
I think nextValidatorSet
should be samplated from next block height, not next round.
Because the validator set is changed each height.
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.
Certainly, this field is used to set and validate the NextValidatorHash value of the block. In the comments, it is described as a validator for the next block.
NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators for the next block
In our version, we cannot select the next Proposer without the cooperation of the first Proposer at the time of the genesis block. Does the transaction processing begin at the next block of genesis (i.e., height 2)? I haven't confirmed it yet, but this code in Tendermint currently assumes that validators
remain unchanged even if the height is incremented, so it may be true.
With such an assumption, I'll increase the height here, not the round.
types/validator_set.go
Outdated
@@ -295,6 +337,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) { | |||
} | |||
if vals.Proposer == nil { | |||
vals.Proposer = vals.findProposer() |
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.
How about removing this code of line.
Because the proposer is elected by ResetProposerAtRandom
using random sampling.
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.
Yes, it's maybe possible. Now that I've checked, it seems that findProposer()
and the functions called by it are immutable, and don't have any side effects. So I'll remove this step.
types/validator_set.go
Outdated
@@ -295,6 +337,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) { | |||
} | |||
if vals.Proposer == nil { | |||
vals.Proposer = vals.findProposer() | |||
vals.ResetProposerAtRandom([]byte{}) |
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.
It seems a little dangerous code.
If proposer is empty, appropriate proposer should be set. But this elect the proposer as using default empty seed
I think it is better to return nil if the vals.Proposer
is empty.
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.
In the current Tendermint code, we'll often find codes where the Proposer changes made without heights or rounds like here. I struggled with how can I rewrite such code, but I'll set nil
so that an explicit error will occur if we use it incorrectly.
06ece55
to
d19a0b1
Compare
d19a0b1
to
66dc287
Compare
consensus/common_test.go
Outdated
@@ -47,7 +47,7 @@ type cleanupFunc func() | |||
// genesis, chain_id, priv_val | |||
var config *cfg.Config // NOTE: must be reset for each _test.go file | |||
var consensusReplayConfig *cfg.Config | |||
var ensureTimeout = time.Millisecond * 100 | |||
var ensureTimeout = time.Millisecond * 1000 |
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.
Tendermint test cases seem to use hundreds of milliseconds for timeout. Did you change it because there was some problem?
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.
When running tests on CircleCI, a test was failing consecutively due to timeouts.
--- FAIL: TestMempoolNoProgressUntilTxsAvailable (0.28s)
panic: Timeout expired while waiting for new activity on the channel [recovered]
panic: Timeout expired while waiting for new activity on the channel
I modified the timeout and confirmed that it was due to a CI environment-specific performance issue. I'm not sure if this 100msec means a response time limit as a Tendermint specification, but I'll bring it back to 100msec.
consensus/state.go
Outdated
@@ -928,6 +931,9 @@ func (cs *State) enterPropose(height int64, round int) { | |||
} | |||
logger.Debug("This node is a validator") | |||
|
|||
// Select the current height and round Proposer | |||
cs.Validators.SelectProposerWithRound(cs.state.LastProofHash, height, 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.
I think SelectProposerWithRound
should be exactly the same as the existing code's IncidenceProposerPriority
location. Shouldn't we call SelectProposerWithRound
here?
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 am also curious.
SelectProposerWithRound
is also called on enterNewRound
What is the reason why it is called twice?
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.
Whereas the previous IncrementProposerPriority
had to be handled carefully because it causes a state change, but SelectProposer
doesn't need to be called as so carefully because it's an idempotent, i.e., the same result for the same parameter. For this reason, I've taken a slightly conservative policy and placed them where rounds may be progressing or regressing in this PR.
However, in enterProposal()
, it was seen that the height
and round
could be entered differently from ConsensusState
, so I put this here, but I'm not sure if this is really necessary here. `IncrementProposerPriority' isn't performed here, so it's may better to remove it and see how it goes. I'll fix this.
@@ -247,8 +240,8 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { | |||
for i, val := range genDoc.Validators { | |||
validators[i] = types.NewValidator(val.PubKey, val.Power) | |||
} | |||
validatorSet = types.NewValidatorSet(validators) | |||
nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) | |||
validatorSet = types.NewRandomValidatorSet(validators, types.MakeRoundHash(genDoc.Hash(), 1, 0)) |
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 have told you this code, but I will think more about whether it is right to make it like this on the genesisState.
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 need to separate about default valudatorSet
and selected validatorSet
of our algorithm.
And I think This validatorSet
of this state
is the all validator set not sampling
And we also need to decide which validatorSet
we store in the block.
I think it it better to store sampling validatorSet
in the block, and use separated property with all validatorSet and sampling validatorSet in the State
of state
and consensus
module.
How about this?
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.
As in the code diff above, in the current Tendermint code, the genesis block and the 2nd block appear to use the same validators. I think it'd enough when we could investigate more detail about it in the future by adding the ability to randomly select a ValidatorSet.
types/validator_set.go
Outdated
@@ -76,6 +85,7 @@ func (vals *ValidatorSet) IsNilOrEmpty() bool { | |||
func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *ValidatorSet { |
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.
Since there are only tests that use CopyIncrementProcessPriority
now, it would be better to remove this function or move it to the test.
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.
Indeed, CopyIncrementProposerPriority()
is no longer used in the production code, so I'll replace it with an alternative code and remove it.
Is it necessary to leave a code calling the IncisionProposerPriority()? I think we can replace the call for |
types/genesis.go
Outdated
@@ -59,7 +59,7 @@ func (genDoc *GenesisDoc) ValidatorHash() []byte { | |||
for i, v := range genDoc.Validators { | |||
vals[i] = NewValidator(v.PubKey, v.Power) | |||
} | |||
vset := NewValidatorSet(vals) | |||
vset := NewRandomValidatorSet(vals, []byte{}) |
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.
How about replacing []byte{}
to genDoc.Hash()
?
the hash of genesis is the hash of GenesisDoc
.
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.
Yes, it may not affect the next step of acquiring for vset.Hash()
but your mention is a formal genesis structure. I'll fix it.
@torao san, AS-ISTO-BEtype ValidatorSet struct {
// NOTE: persisted via reflect, must be exported.
AllValidators []*Validator `json:"validators"`
Validators []*Validator
Proposer *Validator `json:"proposer"`
// cached (unexported)
totalVotingPower int64
} |
@egonspace Now @zemyblue Now I'm not sure that the ValidatorSet passed from ABCI is an all validator in a P2P network. I think we could discuss in a future change rather than in this PR about the advantage of having all validators in a ValidatorSet. |
ad24a5e
to
747e2f9
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.
LGTM
Closes: #48
Description
This PR changes the Proposer selection by the current round-robin PoS to the one based on VRF.
NOTE: With this change in Proposer selection, some of the test cases that were supposed to be deterministic Proposers in the previous round-robin PoS are skipped. These may proceed in a separate PR as they take time to investigate to apply to non-deterministic Proposer selection.
For contributor use:
docs/
) and code commentsFiles changed
in the Github PR explorer