Skip to content

Commit

Permalink
Merge pull request #92 from line/feature/num_of_voters
Browse files Browse the repository at this point in the history
feat: apply calculation of voter count
  • Loading branch information
Woosang Son authored Jun 22, 2020
2 parents f68efb1 + c5ebae7 commit b8b7770
Show file tree
Hide file tree
Showing 28 changed files with 548 additions and 99 deletions.
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
20 changes: 13 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) {
validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
Expand All @@ -814,11 +818,13 @@ 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) {
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
18 changes: 9 additions & 9 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -936,12 +936,7 @@ func (cs *State) enterPropose(height int64, round int) {
}
address := pubKey.Address()

// 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)
return
}

// I'm a proposer, but I might not be a voter
if cs.isProposer(address) {
logger.Info("enterPropose: Our turn to propose",
"proposer",
Expand All @@ -955,6 +950,13 @@ func (cs *State) enterPropose(height int64, round int) {
cs.Proposer.Address,
"privValidator",
cs.privValidator)

}

if !cs.Voters.HasAddress(address) {
logger.Debug("This node is not elected as a voter")
} else {
logger.Debug("This node is elected as a voter")
}
}

Expand Down Expand Up @@ -1462,9 +1464,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
152 changes: 152 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,154 @@ func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpu
}
return sub.Out()
}

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 // NOTE: re-indexing for new voters
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 createProposalBlockByOther(cs *State, other *validatorStub, round int) (
block *types.Block, blockParts *types.PartSet) {
var commit *types.Commit
switch {
case cs.Height == 1:
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
case cs.LastCommit.HasTwoThirdsMajority():
commit = cs.LastCommit.MakeCommit()
default:
return
}

pubKey, err := other.GetPubKey()
if err != nil {
return
}
proposerAddr := pubKey.Address()
message := cs.state.MakeHashMessage(round)

proof, err := other.GenerateVRFProof(message)
if err != nil {
return
}
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr, round, proof)
}

func proposeBlock(t *testing.T, cs *State, round int, vssMap map[crypto.PubKey]*validatorStub) types.BlockID {
newBlock, blockParts := createProposalBlockByOther(cs, vssMap[cs.Proposer.PubKey], round)
proposal := types.NewProposal(cs.Height, round, -1, types.BlockID{
Hash: newBlock.Hash(), PartsHeader: blockParts.Header()})
if err := vssMap[cs.Proposer.PubKey].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)
}
return types.BlockID{Hash: newBlock.Hash(), PartsHeader: blockParts.Header()}
}

func TestStateFullRoundWithSelectedVoter(t *testing.T) {
cs, vss := randStateWithVoterParams(10, &types.VoterParams{
VoterElectionThreshold: 5,
MaxTolerableByzantinePercentage: 20,
ElectionPrecision: 2})
vss[0].Height = 1 // this is needed because of `incrementHeight(vss[1:]...)` of randStateWithVoterParams()
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()) {
blockID := proposeBlock(t, cs, round, vssMap)
ensureProposal(propCh, height, round, blockID)
} 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()) {
blockID := proposeBlock(t, cs, round, vssMap)
ensureProposal(propCh, height, round, blockID)
} 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)

// we're going to roll right into new height
ensureNewRound(newRoundCh, height+1, 0)
}
Loading

0 comments on commit b8b7770

Please sign in to comment.