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 VRF-based Proposer Election #55

Merged
merged 5 commits into from
Apr 14, 2020

Conversation

torao
Copy link
Contributor

@torao torao commented Apr 8, 2020

Closes: #48

Description

This PR changes the Proposer selection by the current round-robin PoS to the one based on VRF.

  1. when a round is changed
  2. when a genesis block is created
  3. when replaying blocks

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:

  • Wrote tests
  • Updated CHANGELOG_PENDING.md
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Updated relevant documentation (docs/) and code comments
  • Re-reviewed Files changed in the Github PR explorer

@torao torao added the C: enhancement Classification: New feature or its request, or improvement in maintainability of code label Apr 8, 2020
@torao torao added this to the Evolve Leader Election into VRF milestone Apr 8, 2020
@torao torao self-assigned this Apr 8, 2020
@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch 3 times, most recently from 14e2321 to 59ad1b1 Compare April 8, 2020 02:25
@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch 2 times, most recently from 127f830 to dbaaa2d Compare April 8, 2020 12:36
@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch from dbaaa2d to 01ca490 Compare April 9, 2020 03:37
state/state.go Outdated
hash := tmhash.New()
hash.Write(state.LastProofHash)
return hash.Sum(b), nil
return MakeRoundHash(state.LastProofHash, state.LastBlockHeight, round), nil
Copy link
Member

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?

Copy link
Contributor Author

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))
Copy link
Member

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.

Copy link
Contributor Author

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 Show resolved Hide resolved
@@ -295,6 +337,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
}
if vals.Proposer == nil {
vals.Proposer = vals.findProposer()
Copy link
Member

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.

Copy link
Contributor Author

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.

@@ -295,6 +337,7 @@ func (vals *ValidatorSet) GetProposer() (proposer *Validator) {
}
if vals.Proposer == nil {
vals.Proposer = vals.findProposer()
vals.ResetProposerAtRandom([]byte{})
Copy link
Member

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.

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 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.

@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch from 06ece55 to d19a0b1 Compare April 10, 2020 12:26
@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch from d19a0b1 to 66dc287 Compare April 10, 2020 12:52
@torao torao changed the title WIP: Introduce VRF-based Proposer Election Introduce VRF-based Proposer Election Apr 10, 2020
@@ -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
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@@ -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)
Copy link
Contributor

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?

Copy link
Member

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?

Copy link
Contributor Author

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))
Copy link
Contributor

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

@@ -76,6 +85,7 @@ func (vals *ValidatorSet) IsNilOrEmpty() bool {
func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *ValidatorSet {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@egonspace
Copy link
Contributor

Is it necessary to leave a code calling the IncisionProposerPriority()? I think we can replace the call for IncrementProposerPriority() with a code that calls SelectProposerWithRound().

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{})
Copy link
Member

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.

Copy link
Contributor Author

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.

@zemyblue
Copy link
Member

zemyblue commented Apr 13, 2020

@torao san,
How about save all valistors and sampling validators into the types.ValidatorSet like below?
And change the function logics accordingly.
Because, to separate all validator and sampling validators

AS-IS

https://github.com/line/tendermint/blob/66dc287b945f94d536df4c9c8a9471b90cbf94ca/types/validator_set.go#L44-L51

TO-BE

type ValidatorSet struct {
	// NOTE: persisted via reflect, must be exported.
	AllValidators []*Validator `json:"validators"`
        Validators []*Validator
	Proposer   *Validator   `json:"proposer"`

	// cached (unexported)
	totalVotingPower int64
}

@torao
Copy link
Contributor Author

torao commented Apr 13, 2020

@egonspace Now IncrementsProposerPriority() doesn't affect our process because we are using Proposer selection based on VotingPower. But I keep it for when we consider the selection of a Proposer based on ProposerPriority. In this PR, I'll write a TODO to describe why this function is left.

@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.

@torao torao force-pushed the feature/introduce_vrf_based_proposer_election branch from ad24a5e to 747e2f9 Compare April 13, 2020 09:06
Copy link
Member

@zemyblue zemyblue left a comment

Choose a reason for hiding this comment

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

LGTM

@torao torao merged commit 37f0c66 into develop Apr 14, 2020
@zemyblue zemyblue mentioned this pull request Apr 14, 2020
5 tasks
zemyblue added a commit that referenced this pull request Apr 17, 2020
@zemyblue zemyblue deleted the feature/introduce_vrf_based_proposer_election branch April 20, 2020 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: enhancement Classification: New feature or its request, or improvement in maintainability of code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants