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

feat: apply calculation of voter count #92

Merged
merged 16 commits into from
Jun 22, 2020
4 changes: 4 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### BREAKING CHANGES:

- State
- [state] [\#92](https://github.com/line/tendermint/pull/92) Genesis state

- CLI/RPC/Config

- Apps
Expand All @@ -18,6 +21,7 @@
### FEATURES:
- [rpc] [\#78](https://github.com/line/tendermint/pull/78) Add `Voters` rpc
- [consensus] [\#83](https://github.com/line/tendermint/pull/83) Selection voters using random sampling without replacement
- [consensus] [\#92](https://github.com/line/tendermint/pull/92) Apply calculation of voter count

### IMPROVEMENTS:

Expand Down
1 change: 0 additions & 1 deletion blockchain/v1/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced
}

func TestFastSyncNoBlockResponse(t *testing.T) {

config = cfg.ResetTestRoot("blockchain_new_reactor_test")
defer os.RemoveAll(config.RootDir)
genDoc, privVals := randGenesisDoc(1, false, 30)
Expand Down
1 change: 0 additions & 1 deletion blockchain/v2/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ func TestReactorHelperMode(t *testing.T) {
var (
channelID = byte(0x40)
)

config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
defer os.RemoveAll(config.RootDir)
genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
Expand Down
1 change: 1 addition & 0 deletions cmd/tendermint/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func initFilesWithConfig(config *cfg.Config) error {
ChainID: fmt.Sprintf("test-chain-%v", tmrand.Str(6)),
GenesisTime: tmtime.Now(),
ConsensusParams: types.DefaultConsensusParams(),
VoterParams: types.DefaultVoterParams(),
}
pubKey, err := pv.GetPubKey()
if err != nil {
Expand Down
19 changes: 12 additions & 7 deletions consensus/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,12 @@ func loadPrivValidator(config *cfg.Config) *privval.FilePV {
}

func randState(nValidators int) (*State, []*validatorStub) {
return randStateWithVoterParams(nValidators, types.DefaultVoterParams())
}

func randStateWithVoterParams(nValidators int, voterParams *types.VoterParams) (*State, []*validatorStub) {
// Get State
state, privVals := randGenesisState(nValidators, false, 10)
state, privVals := randGenesisState(nValidators, false, 10, voterParams)
state.LastProofHash = []byte{2}

vss := make([]*validatorStub, nValidators)
Expand All @@ -433,7 +437,7 @@ func theOthers(index int) int {
}

func forceProposer(cs *State, vals []*validatorStub, index []int, height []int64, round []int) {
for i := 0; i < 1000; i++ {
for i := 0; i < 5000; i++ {
allMatch := true
firstHash := []byte{byte(i)}
currentHash := firstHash
Expand Down Expand Up @@ -697,7 +701,7 @@ func consensusLogger() log.Logger {

func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker,
appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*State, cleanupFunc) {
genDoc, privVals := randGenesisDoc(nValidators, false, 30)
genDoc, privVals := randGenesisDoc(nValidators, false, 30, types.DefaultVoterParams())
css := make([]*State, nValidators)
logger := consensusLogger()
configRootDirs := make([]string, 0, nValidators)
Expand Down Expand Up @@ -735,7 +739,7 @@ func randConsensusNetWithPeers(
tickerFunc func() TimeoutTicker,
appFunc func(string) abci.Application,
) ([]*State, *types.GenesisDoc, *cfg.Config, cleanupFunc) {
genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower)
genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower, types.DefaultVoterParams())
css := make([]*State, nPeers)
logger := consensusLogger()
var peer0Config *cfg.Config
Expand Down Expand Up @@ -797,7 +801,7 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
//-------------------------------------------------------------------------------
// genesis

func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
func randGenesisDoc(numValidators int, randPower bool, minPower int64, voterParams *types.VoterParams) (*types.GenesisDoc, []types.PrivValidator) {
egonspace marked this conversation as resolved.
Show resolved Hide resolved
validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
Expand All @@ -814,11 +818,12 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
GenesisTime: tmtime.Now(),
ChainID: config.ChainID(),
Validators: validators,
VoterParams: voterParams,
}, privValidators
}

func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
func randGenesisState(numValidators int, randPower bool, minPower int64, voterParams *types.VoterParams) (sm.State, []types.PrivValidator) {
egonspace marked this conversation as resolved.
Show resolved Hide resolved
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower, voterParams)
s0, _ := sm.MakeGenesisState(genDoc)
return s0, privValidators
}
Expand Down
10 changes: 5 additions & 5 deletions consensus/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
defer os.RemoveAll(config.RootDir)
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
state, privVals := randGenesisState(1, false, 10, nil)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
height, round := cs.Height, cs.Round
Expand All @@ -46,7 +46,7 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
defer os.RemoveAll(config.RootDir)
config.Consensus.CreateEmptyBlocksInterval = ensureTimeout
state, privVals := randGenesisState(1, false, 10)
state, privVals := randGenesisState(1, false, 10, nil)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
height, round := cs.Height, cs.Round
Expand All @@ -62,7 +62,7 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
defer os.RemoveAll(config.RootDir)
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
state, privVals := randGenesisState(1, false, 10, nil)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
height, round := cs.Height, cs.Round
Expand Down Expand Up @@ -108,7 +108,7 @@ func deliverTxsRange(cs *State, start, end int) {
}

func TestMempoolTxConcurrentWithCommit(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
state, privVals := randGenesisState(1, false, 10, nil)
blockDB := dbm.NewMemDB()
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], NewCounterApplication(), blockDB)
sm.SaveState(blockDB, state)
Expand All @@ -130,7 +130,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
}

func TestMempoolRmBadTx(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
state, privVals := randGenesisState(1, false, 10, nil)
app := NewCounterApplication()
blockDB := dbm.NewMemDB()
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], app, blockDB)
Expand Down
2 changes: 1 addition & 1 deletion consensus/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func TestReactorWithEvidence(t *testing.T) {
// to unroll unwieldy abstractions. Here we duplicate the code from:
// css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)

genDoc, privVals := randGenesisDoc(nValidators, false, 30)
genDoc, privVals := randGenesisDoc(nValidators, false, 30, nil)
css := make([]*State, nValidators)
logger := consensusLogger()
for i := 0; i < nValidators; i++ {
Expand Down
4 changes: 2 additions & 2 deletions consensus/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ func (h *Handshaker) ReplayBlocks(
state.Voters = types.ToVoterAll(state.Validators.Validators)
// Should sync it with MakeGenesisState()
state.NextValidators = types.NewValidatorSet(vals)
state.NextVoters = types.SelectVoter(state.NextValidators, h.genDoc.Hash())
state.NextVoters = types.SelectVoter(state.NextValidators, h.genDoc.Hash(), state.VoterParams)
} else if len(h.genDoc.Validators) == 0 {
// If validator set is not set in genesis and still empty after InitChain, exit.
return nil, fmt.Errorf("validator set is nil in genesis and still empty after InitChain")
Expand Down Expand Up @@ -450,7 +450,7 @@ func (h *Handshaker) replayBlocks(
assertAppHashEqualsOneFromBlock(appHash, block)
}

appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateDB)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateDB, state.VoterParams)
if err != nil {
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions consensus/replay_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
mempool, evpool := mock.Mempool{}, sm.MockEvidencePool{}
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)

consensusState := NewState(csConfig, state.Copy(), blockExec,
blockStore, mempool, evpool)
consensusState := NewState(csConfig, state.Copy(), blockExec, blockStore, mempool, evpool)

consensusState.SetEventBus(eventBus)
return consensusState
Expand Down
7 changes: 3 additions & 4 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -938,9 +938,10 @@ func (cs *State) enterPropose(height int64, round int) {

// if not a validator, we're done
if !cs.Voters.HasAddress(address) {
logger.Debug("This node is not a validator", "addr", address, "vals", cs.Voters)
logger.Debug("This node is not elected as a voter", "addr", address, "vals", cs.Voters)
return
}
logger.Debug("This node is elected as a voter")

if cs.isProposer(address) {
logger.Info("enterPropose: Our turn to propose",
Expand Down Expand Up @@ -1462,9 +1463,7 @@ func (cs *State) finalizeCommit(height int64) {
var err error
var retainHeight int64
stateCopy, retainHeight, err = cs.blockExec.ApplyBlock(
stateCopy,
types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()},
block)
stateCopy, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}, block)
if err != nil {
cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "err", err)
err := tmos.Kill()
Expand Down
147 changes: 147 additions & 0 deletions consensus/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"

cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
tmrand "github.com/tendermint/tendermint/libs/rand"
Expand Down Expand Up @@ -1845,3 +1846,149 @@ func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpu
}
return sub.Out()
}

func setProposerPrivVal(cs *State, vssMap map[crypto.PubKey]*validatorStub) {
proposer := cs.Validators.SelectProposer(cs.state.LastProofHash, cs.Height, cs.Round).PubKey
cs.privValidator = vssMap[proposer]
if cs.privValidator == nil {
panic("Cannot find proposer among vss")
}
}

func makeVssMap(vss []*validatorStub) map[crypto.PubKey]*validatorStub {
vssMap := make(map[crypto.PubKey]*validatorStub)
for _, pv := range vss {
pubKey, _ := pv.GetPubKey()
vssMap[pubKey] = pv
}
return vssMap
}

func votersPrivVals(voterSet *types.VoterSet, vssMap map[crypto.PubKey]*validatorStub) []*validatorStub {
totalVotingPower := voterSet.TotalVotingPower()
votingPower := int64(0)
voters := 0
for i, v := range voterSet.Voters {
vssMap[v.PubKey].Index = i
if votingPower < totalVotingPower*2/3+1 {
votingPower += v.VotingPower
voters++
}
}
result := make([]*validatorStub, voters)
for i := 0; i < voters; i++ {
result[i] = vssMap[voterSet.Voters[i].PubKey]
}
return result
}

func TestStateFullRoundWithSelectedVoter(t *testing.T) {
cs, vss := randStateWithVoterParams(10, &types.VoterParams{
VoterElectionThreshold: 5,
MaxByzantineTolerancePercentage: 20,
AccuracyPrecision: 1})
vss[0].Height = 1
vssMap := makeVssMap(vss)
height, round := cs.Height, cs.Round

voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote)
propCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound)
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)

startTestRound(cs, height, round)

// height 1
ensureNewRound(newRoundCh, height, round)
privPubKey, _ := cs.privValidator.GetPubKey()
if !cs.isProposer(privPubKey.Address()) {
orgPrivVal := cs.privValidator
setProposerPrivVal(cs, vssMap)
newBlock, blockParts := cs.createProposalBlock(round)
proposal := types.NewProposal(cs.Height, round, -1, types.BlockID{
egonspace marked this conversation as resolved.
Show resolved Hide resolved
newBlock.Hash(), blockParts.Header()})
if err := cs.privValidator.SignProposal(config.ChainID(), proposal); err != nil {
t.Fatal("failed to sign bad proposal", err)
}
// set the proposal block
if err := cs.SetProposalAndBlock(proposal, newBlock, blockParts, "some peer"); err != nil {
t.Fatal(err)
}
blockID := types.BlockID{Hash: newBlock.Hash(), PartsHeader: blockParts.Header()}
ensureProposal(propCh, height, round, blockID)
cs.privValidator = orgPrivVal
} else {
ensureNewProposal(propCh, height, round)
}

propBlock := cs.GetRoundState().ProposalBlock
voters := cs.Voters
voterPrivVals := votersPrivVals(voters, vssMap)
signAddVotes(cs, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(types.BlockPartSizeBytes).Header(),
voterPrivVals...)

for range voterPrivVals {
ensurePrevote(voteCh, height, round) // wait for prevote
}

signAddVotes(cs, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(types.BlockPartSizeBytes).Header(),
voterPrivVals...)

for range voterPrivVals {
ensurePrecommit(voteCh, height, round) // wait for precommit
}

ensureNewBlock(newBlockCh, height)

// height 2
incrementHeight(vss...)

ensureNewRound(newRoundCh, height+1, 0)

height = cs.Height
privPubKey, _ = cs.privValidator.GetPubKey()
if !cs.isProposer(privPubKey.Address()) {
orgPrivVal := cs.privValidator
setProposerPrivVal(cs, vssMap)
newBlock, blockParts := cs.createProposalBlock(round)
proposal := types.NewProposal(cs.Height, round, -1, types.BlockID{
egonspace marked this conversation as resolved.
Show resolved Hide resolved
newBlock.Hash(), blockParts.Header()})
if err := cs.privValidator.SignProposal(config.ChainID(), proposal); err != nil {
t.Fatal("failed to sign bad proposal", err)
}
// set the proposal block
if err := cs.SetProposalAndBlock(proposal, newBlock, blockParts, "some peer"); err != nil {
t.Fatal(err)
}
blockID := types.BlockID{Hash: newBlock.Hash(), PartsHeader: blockParts.Header()}
ensureProposal(propCh, height, round, blockID)
cs.privValidator = orgPrivVal
} else {
ensureNewProposal(propCh, height, round)
}

propBlock = cs.GetRoundState().ProposalBlock
voters = cs.Voters
voterPrivVals = votersPrivVals(voters, vssMap)

signAddVotes(cs, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(types.BlockPartSizeBytes).Header(),
voterPrivVals...)

for range voterPrivVals {
ensurePrevote(voteCh, height, round) // wait for prevote
}

signAddVotes(cs, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(types.BlockPartSizeBytes).Header(),
voterPrivVals...)

for range voterPrivVals {
ensurePrecommit(voteCh, height, round) // wait for precommit
}

ensureNewBlock(newBlockCh, height)

//setProposerPrivVal(cs, vssMap)

// we're going to roll right into new height
ensureNewRound(newRoundCh, height+1, 0)
}
2 changes: 1 addition & 1 deletion evidence/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {

// fetch the validator and return its voting power as its priority
// TODO: something better ?
valSet, _, err := sm.LoadValidators(evpool.stateDB, evidence.Height())
valSet, err := sm.LoadValidators(evpool.stateDB, evidence.Height())
if err != nil {
return err
}
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ require (
github.com/tendermint/tm-db v0.5.1
github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 // indirect
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
golang.org/x/tools v0.0.0-20200519205726-57a9e4404bf7 // indirect
google.golang.org/grpc v1.28.1
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86
)
Loading