From b8261cf337b67d7260bbc584c47f3e5ec06c76b3 Mon Sep 17 00:00:00 2001 From: Woosang Son Date: Mon, 27 Apr 2020 16:12:06 +0900 Subject: [PATCH] feat: election of ValidatorSet based on VRF #74 feat: add voteset to state feat: implement VoterSet fix: test failure fix: change validators to voters more feat: implement select voters feat: implement RandomSamplingToMax feat: add test case feat: more test fix: fmt check failure fix: circleci failure fix: randValidator may create a validator having 0 voting power fix: elect a proposer among validators not among voters fix: apply comment; proposer must be found in validators fix: apply comments fix: contracts_test failure fix: contracts_test failure fix: apply comments --- blockchain/v0/reactor.go | 2 +- blockchain/v0/reactor_test.go | 2 +- blockchain/v1/reactor.go | 2 +- blockchain/v1/reactor_test.go | 2 +- blockchain/v2/processor_context.go | 2 +- blockchain/v2/reactor_test.go | 2 +- cmd/contract_tests/unmarshaler/unmarshal.go | 1 + consensus/byzantine_test.go | 16 +- consensus/common_test.go | 2 +- consensus/metrics.go | 109 ++-- consensus/reactor.go | 28 +- consensus/reactor_test.go | 24 +- consensus/replay.go | 5 +- consensus/replay_file.go | 4 +- consensus/replay_test.go | 10 +- consensus/state.go | 58 ++- consensus/state_test.go | 6 +- consensus/types/height_vote_set.go | 18 +- consensus/types/height_vote_set_test.go | 2 +- consensus/types/round_state.go | 29 +- evidence/pool.go | 6 +- evidence/pool_test.go | 21 +- evidence/reactor_test.go | 2 +- evidence/verify.go | 22 +- evidence/verify_test.go | 87 ++-- libs/rand/sampling.go | 50 ++ libs/rand/sampling_test.go | 70 ++- light/client.go | 8 +- light/client_test.go | 64 +-- light/detector.go | 6 +- light/detector_test.go | 27 +- light/helpers_test.go | 46 +- light/provider/http/http.go | 29 +- light/provider/mock/mock.go | 18 +- light/proxy/routes.go | 6 +- light/rpc/client.go | 22 +- light/store/db/db_test.go | 32 +- light/verifier.go | 22 +- light/verifier_test.go | 27 +- node/node_test.go | 2 +- proto/tendermint/state/types.pb.go | 425 +++++++++++----- proto/tendermint/state/types.proto | 12 +- proto/tendermint/types/types.pb.go | 244 ++++----- proto/tendermint/types/types.proto | 14 +- proto/tendermint/types/validator.pb.go | 268 +++++++++- proto/tendermint/types/validator.proto | 5 + rpc/client/evidence_test.go | 2 +- rpc/client/http/http.go | 8 +- rpc/client/interface.go | 2 +- rpc/client/local/local.go | 4 +- rpc/client/mock/client.go | 4 +- rpc/client/mocks/client.go | 10 +- rpc/client/rpc_test.go | 4 +- rpc/core/consensus.go | 35 +- rpc/core/doc.go | 3 +- rpc/core/routes.go | 2 +- rpc/core/status.go | 2 +- rpc/core/types/responses.go | 9 +- rpc/openapi/openapi.yaml | 44 +- state/execution.go | 20 +- state/execution_test.go | 39 +- state/export_test.go | 4 +- state/helpers_test.go | 4 +- state/mocks/store.go | 21 +- state/state.go | 32 +- state/state_test.go | 49 +- state/store.go | 42 +- state/store_test.go | 24 +- state/validation.go | 24 +- state/validation_test.go | 14 +- statesync/stateprovider.go | 4 +- statesync/syncer_test.go | 4 +- store/store_test.go | 2 +- test/e2e/tests/validator_test.go | 4 +- test/maverick/consensus/state.go | 31 +- types/block.go | 73 +-- types/block_test.go | 140 ++--- types/evidence.go | 18 +- types/evidence_test.go | 50 +- types/light.go | 23 +- types/light_test.go | 66 +-- types/protobuf.go | 10 +- types/validator_set.go | 307 +---------- types/validator_set_test.go | 102 ++-- types/vote_set.go | 22 +- types/vote_set_test.go | 19 +- types/voter_set.go | 537 ++++++++++++++++++++ types/voter_set_test.go | 113 ++++ 88 files changed, 2412 insertions(+), 1374 deletions(-) create mode 100644 types/voter_set.go create mode 100644 types/voter_set_test.go diff --git a/blockchain/v0/reactor.go b/blockchain/v0/reactor.go index 81fad4932..729cbde64 100644 --- a/blockchain/v0/reactor.go +++ b/blockchain/v0/reactor.go @@ -367,7 +367,7 @@ FOR_LOOP: // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is // currently necessary. - err := state.Validators.VerifyCommitLight( + err := state.Voters.VerifyCommitLight( chainID, firstID, first.Height, second.LastCommit) if err != nil { bcR.Logger.Error("Error in validation", "err", err) diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 5dc250d05..1b23105bb 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -292,7 +292,7 @@ func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCo message := state.MakeHashMessage(0) proof, _ := privVal.GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, - types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address, 0, proof) + state.Validators.SelectProposer(state.LastProofHash, height, 0).Address, 0, proof) return block } diff --git a/blockchain/v1/reactor.go b/blockchain/v1/reactor.go index c4c61ec51..dfb457d73 100644 --- a/blockchain/v1/reactor.go +++ b/blockchain/v1/reactor.go @@ -472,7 +472,7 @@ func (bcR *BlockchainReactor) processBlock() error { // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is // currently necessary. - err = bcR.state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit) + err = bcR.state.Voters.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit) if err != nil { bcR.Logger.Error("error during commit verification", "err", err, "first", first.Height, "second", second.Height) diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index c10091e63..b3fab3836 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -359,7 +359,7 @@ func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCo message := state.MakeHashMessage(0) proof, _ := privVal.GenerateVRFProof(message) block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, - types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address, 0, proof) + state.Validators.SelectProposer(state.LastProofHash, height, 0).Address, 0, proof) return block } diff --git a/blockchain/v2/processor_context.go b/blockchain/v2/processor_context.go index 6a0466550..4389e1989 100644 --- a/blockchain/v2/processor_context.go +++ b/blockchain/v2/processor_context.go @@ -44,7 +44,7 @@ func (pc *pContext) setState(state state.State) { } func (pc pContext) verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error { - return pc.state.Validators.VerifyCommitLight(chainID, blockID, height, commit) + return pc.state.Voters.VerifyCommitLight(chainID, blockID, height, commit) } func (pc *pContext) saveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index 70fe5a200..018622806 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -455,7 +455,7 @@ func makeTxs(height int64) (txs []types.Tx) { func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCommit *types.Commit) *types.Block { message := state.MakeHashMessage(0) proof, _ := privVal.GenerateVRFProof(message) - proposerAddr := types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address + proposerAddr := state.Validators.SelectProposer(state.LastProofHash, height, 0).Address block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr, 0, proof) return block } diff --git a/cmd/contract_tests/unmarshaler/unmarshal.go b/cmd/contract_tests/unmarshaler/unmarshal.go index d336c9009..a3ff69928 100644 --- a/cmd/contract_tests/unmarshaler/unmarshal.go +++ b/cmd/contract_tests/unmarshaler/unmarshal.go @@ -2,6 +2,7 @@ package unmarshaler import ( "encoding/json" + "gopkg.in/yaml.v3" ) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index bffc6decc..f0c038277 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -318,7 +318,7 @@ func TestByzantine(t *testing.T) { // enable txs so we can create different proposals assertMempool(css[i].txNotifier).EnableTxsAvailable() // make first val byzantine - if int32(i) == proposerIdx { + if i == proposerIdx { // NOTE: Now, test validators are MockPV, which by default doesn't // do any safety checks. css[i].privValidator.(*types.MockPV).DisableChecks() @@ -346,7 +346,7 @@ func TestByzantine(t *testing.T) { var conRI p2p.Reactor = conR // make first val byzantine - if int32(i) == proposerIdx { + if i == proposerIdx { conRI = NewByzantineReactor(conR) } @@ -373,7 +373,7 @@ func TestByzantine(t *testing.T) { return switches[i] }, func(sws []*p2p.Switch, i, j int) { // the network starts partitioned with globally active adversary - if int32(i) != proposerIdx && int32(j) != proposerIdx { + if i != proposerIdx && j != proposerIdx { return } p2p.Connect2Switches(sws, i, j) @@ -382,7 +382,7 @@ func TestByzantine(t *testing.T) { // start the non-byz state machines. // note these must be started before the byz for i := 0; i < N; i++ { - if int32(i) != proposerIdx { + if i != proposerIdx { cr := reactors[i].(*Reactor) cr.SwitchToConsensus(cr.conS.GetState(), false) } @@ -418,7 +418,7 @@ func TestByzantine(t *testing.T) { // (one of them already has) wg := new(sync.WaitGroup) for i := 0; i < N-1; i++ { - if int32(i) != proposerIdx { + if i != proposerIdx { wg.Add(1) go func(j int) { <-blocksSubs[j].Out() @@ -446,9 +446,9 @@ func TestByzantine(t *testing.T) { } // find proposer of current height and round from State -func findProposer(state *State) (int32, *types.Validator) { - proposer := types.SelectProposer(state.Validators, state.state.LastProofHash, state.Height, state.Round) - return state.Validators.GetByAddress(proposer.PubKey.Address()) +func findProposer(state *State) (int, *types.Validator) { + proposer := state.Validators.SelectProposer(state.state.LastProofHash, state.Height, state.Round) + return state.Voters.GetByAddress(proposer.PubKey.Address()) } //------------------------------- diff --git a/consensus/common_test.go b/consensus/common_test.go index 43c049b3d..6ca9f29ea 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -467,7 +467,7 @@ func forceProposer(cs *State, vals []*validatorStub, index []int, height []int64 mustBe = false } pubKey, _ := curVal.GetPubKey() - if pubKey.Equals(types.SelectProposer(cs.Validators, currentHash, height[j], round[j]).PubKey) != + if pubKey.Equals(cs.Validators.SelectProposer(currentHash, height[j], round[j]).PubKey) != mustBe { allMatch = false break diff --git a/consensus/metrics.go b/consensus/metrics.go index bbd823a3f..21493ed47 100644 --- a/consensus/metrics.go +++ b/consensus/metrics.go @@ -19,28 +19,29 @@ type Metrics struct { // Height of the chain. Height metrics.Gauge - // ValidatorLastSignedHeight of a validator. - ValidatorLastSignedHeight metrics.Gauge + // VoterLastSignedHeight of a voter. + VoterLastSignedHeight metrics.Gauge // Number of rounds. Rounds metrics.Gauge - // Number of validators. - Validators metrics.Gauge - // Total power of all validators. - ValidatorsPower metrics.Gauge - // Power of a validator. - ValidatorPower metrics.Gauge - // Amount of blocks missed by a validator. - ValidatorMissedBlocks metrics.Gauge - // Number of validators who did not sign. - MissingValidators metrics.Gauge - // Total power of the missing validators. - MissingValidatorsPower metrics.Gauge - // Number of validators who tried to double sign. - ByzantineValidators metrics.Gauge - // Total power of the byzantine validators. - ByzantineValidatorsPower metrics.Gauge + // ValidatorOrVoter: voter + // Number of voters. + Voters metrics.Gauge + // Total power of all voters. + VotersPower metrics.Gauge + // Power of a voter. + VoterPower metrics.Gauge + // Amount of blocks missed by a voter. + VoterMissedBlocks metrics.Gauge + // Number of voters who did not sign. + MissingVoters metrics.Gauge + // Total power of the missing voters. + MissingVotersPower metrics.Gauge + // Number of voters who tried to double sign. + ByzantineVoters metrics.Gauge + // Total power of the byzantine voters. + ByzantineVotersPower metrics.Gauge // Time between this and the last block. BlockIntervalSeconds metrics.Histogram @@ -84,59 +85,59 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Help: "Number of rounds.", }, labels).With(labelsAndValues...), - Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + Voters: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "validators", - Help: "Number of validators.", + Name: "voters", + Help: "Number of voters.", }, labels).With(labelsAndValues...), - ValidatorLastSignedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + VoterLastSignedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "validator_last_signed_height", - Help: "Last signed height for a validator", + Name: "voter_last_signed_height", + Help: "Last signed height for a voter", }, append(labels, "validator_address")).With(labelsAndValues...), - ValidatorMissedBlocks: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + VoterMissedBlocks: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "validator_missed_blocks", - Help: "Total missed blocks for a validator", + Name: "voter_missed_blocks", + Help: "Total missed blocks for a voter", }, append(labels, "validator_address")).With(labelsAndValues...), - ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + VotersPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "validators_power", - Help: "Total power of all validators.", + Name: "voters_power", + Help: "Total power of all voters.", }, labels).With(labelsAndValues...), - ValidatorPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + VoterPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "validator_power", - Help: "Power of a validator", + Name: "voter_power", + Help: "Power of a voter", }, append(labels, "validator_address")).With(labelsAndValues...), - MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + MissingVoters: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "missing_validators", - Help: "Number of validators who did not sign.", + Name: "missing_voters", + Help: "Number of voters who did not sign.", }, labels).With(labelsAndValues...), - MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + MissingVotersPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "missing_validators_power", - Help: "Total power of the missing validators.", + Name: "missing_voters_power", + Help: "Total power of the missing voters.", }, labels).With(labelsAndValues...), - ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + ByzantineVoters: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "byzantine_validators", - Help: "Number of validators who tried to double sign.", + Name: "byzantine_voters", + Help: "Number of voters who tried to double sign.", }, labels).With(labelsAndValues...), - ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + ByzantineVotersPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, - Name: "byzantine_validators_power", - Help: "Total power of the byzantine validators.", + Name: "byzantine_voters_power", + Help: "Total power of the byzantine voters.", }, labels).With(labelsAndValues...), BlockIntervalSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ Namespace: namespace, @@ -194,18 +195,18 @@ func NopMetrics() *Metrics { return &Metrics{ Height: discard.NewGauge(), - ValidatorLastSignedHeight: discard.NewGauge(), + VoterLastSignedHeight: discard.NewGauge(), Rounds: discard.NewGauge(), - Validators: discard.NewGauge(), - ValidatorsPower: discard.NewGauge(), - ValidatorPower: discard.NewGauge(), - ValidatorMissedBlocks: discard.NewGauge(), - MissingValidators: discard.NewGauge(), - MissingValidatorsPower: discard.NewGauge(), - ByzantineValidators: discard.NewGauge(), - ByzantineValidatorsPower: discard.NewGauge(), + Voters: discard.NewGauge(), + VotersPower: discard.NewGauge(), + VoterPower: discard.NewGauge(), + VoterMissedBlocks: discard.NewGauge(), + MissingVoters: discard.NewGauge(), + MissingVotersPower: discard.NewGauge(), + ByzantineVoters: discard.NewGauge(), + ByzantineVotersPower: discard.NewGauge(), BlockIntervalSeconds: discard.NewHistogram(), diff --git a/consensus/reactor.go b/consensus/reactor.go index d7d274002..9f1f12dea 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -328,9 +328,9 @@ func (conR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { case *VoteMessage: cs := conR.conS cs.mtx.RLock() - height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size() + height, voterSize, lastCommitSize := cs.Height, cs.Voters.Size(), cs.LastCommit.Size() cs.mtx.RUnlock() - ps.EnsureVoteBitArrays(height, valSize) + ps.EnsureVoteBitArrays(height, voterSize) ps.EnsureVoteBitArrays(height-1, lastCommitSize) ps.SetHasVote(msg.Vote) @@ -1140,7 +1140,7 @@ func (ps *PeerState) getVoteBitArray(height int64, round int32, votesType tmprot } // 'round': A round for which we have a +2/3 commit. -func (ps *PeerState) ensureCatchupCommitRound(height int64, round int32, numValidators int) { +func (ps *PeerState) ensureCatchupCommitRound(height int64, round int32, numVoters int) { if ps.PRS.Height != height { return } @@ -1164,37 +1164,37 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int32, numVali if round == ps.PRS.Round { ps.PRS.CatchupCommit = ps.PRS.Precommits } else { - ps.PRS.CatchupCommit = bits.NewBitArray(numValidators) + ps.PRS.CatchupCommit = bits.NewBitArray(numVoters) } } // EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking // what votes this peer has received. -// NOTE: It's important to make sure that numValidators actually matches -// what the node sees as the number of validators for height. -func (ps *PeerState) EnsureVoteBitArrays(height int64, numValidators int) { +// NOTE: It's important to make sure that numVoters actually matches +// what the node sees as the number of voters for height. +func (ps *PeerState) EnsureVoteBitArrays(height int64, numVoters int) { ps.mtx.Lock() defer ps.mtx.Unlock() - ps.ensureVoteBitArrays(height, numValidators) + ps.ensureVoteBitArrays(height, numVoters) } -func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) { +func (ps *PeerState) ensureVoteBitArrays(height int64, numVoters int) { if ps.PRS.Height == height { if ps.PRS.Prevotes == nil { - ps.PRS.Prevotes = bits.NewBitArray(numValidators) + ps.PRS.Prevotes = bits.NewBitArray(numVoters) } if ps.PRS.Precommits == nil { - ps.PRS.Precommits = bits.NewBitArray(numValidators) + ps.PRS.Precommits = bits.NewBitArray(numVoters) } if ps.PRS.CatchupCommit == nil { - ps.PRS.CatchupCommit = bits.NewBitArray(numValidators) + ps.PRS.CatchupCommit = bits.NewBitArray(numVoters) } if ps.PRS.ProposalPOL == nil { - ps.PRS.ProposalPOL = bits.NewBitArray(numValidators) + ps.PRS.ProposalPOL = bits.NewBitArray(numVoters) } } else if ps.PRS.Height == height+1 { if ps.PRS.LastCommit == nil { - ps.PRS.LastCommit = bits.NewBitArray(numValidators) + ps.PRS.LastCommit = bits.NewBitArray(numVoters) } } } diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 2fdf79364..8b45f89de 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -353,48 +353,48 @@ func TestReactorVotingPowerChange(t *testing.T) { val1PubKeyABCI, err := cryptoenc.PubKeyToProto(val1PubKey) require.NoError(t, err) updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKeyABCI, 25) - previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() + previousTotalVotingPower := css[0].GetRoundState().LastVoters.TotalVotingPower() waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) - if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { + if css[0].GetRoundState().LastVoters.TotalVotingPower() == previousTotalVotingPower { t.Fatalf( "expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, - css[0].GetRoundState().LastValidators.TotalVotingPower()) + css[0].GetRoundState().LastVoters.TotalVotingPower()) } updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 2) - previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() + previousTotalVotingPower = css[0].GetRoundState().LastVoters.TotalVotingPower() waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) - if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { + if css[0].GetRoundState().LastVoters.TotalVotingPower() == previousTotalVotingPower { t.Fatalf( "expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, - css[0].GetRoundState().LastValidators.TotalVotingPower()) + css[0].GetRoundState().LastVoters.TotalVotingPower()) } updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 26) - previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() + previousTotalVotingPower = css[0].GetRoundState().LastVoters.TotalVotingPower() waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) - if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { + if css[0].GetRoundState().LastVoters.TotalVotingPower() == previousTotalVotingPower { t.Fatalf( "expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, - css[0].GetRoundState().LastValidators.TotalVotingPower()) + css[0].GetRoundState().LastVoters.TotalVotingPower()) } } @@ -464,18 +464,18 @@ func TestReactorValidatorSetChanges(t *testing.T) { updatePubKey1ABCI, err := cryptoenc.PubKeyToProto(updateValidatorPubKey1) require.NoError(t, err) updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) - previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower() + previousTotalVotingPower := css[nVals].GetRoundState().LastVoters.TotalVotingPower() waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css, updateValidatorTx1) waitForAndValidateBlockWithTx(t, nPeers, activeVals, blocksSubs, css, updateValidatorTx1) waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css) waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, blocksSubs, css) - if css[nVals].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { + if css[nVals].GetRoundState().LastVoters.TotalVotingPower() == previousTotalVotingPower { t.Errorf( "expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, - css[nVals].GetRoundState().LastValidators.TotalVotingPower()) + css[nVals].GetRoundState().LastVoters.TotalVotingPower()) } //--------------------------------------------------------------------------- diff --git a/consensus/replay.go b/consensus/replay.go index 8800cf14d..d99a935c0 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -313,7 +313,7 @@ func (h *Handshaker) ReplayBlocks( ChainId: h.genDoc.ChainID, InitialHeight: h.genDoc.InitialHeight, ConsensusParams: csParams, - Validators: nextVals, + Validators: nextVals, // ValidatorOrVoter: validator AppStateBytes: h.genDoc.AppState, } res, err := proxyApp.Consensus().InitChainSync(req) @@ -337,7 +337,10 @@ func (h *Handshaker) ReplayBlocks( return nil, err } state.Validators = types.NewValidatorSet(vals) + state.Voters = types.ToVoterAll(state.Validators) + // Should sync it with MakeGenesisState() state.NextValidators = types.NewValidatorSet(vals) + state.NextVoters = types.SelectVoter(state.NextValidators, h.genDoc.Hash()) } 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") diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 4bf7466ab..d3583685a 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -257,8 +257,8 @@ func (pb *playback) replayConsoleLoop() int { switch tokens[1] { case "short": fmt.Printf("%v/%v/%v\n", rs.Height, rs.Round, rs.Step) - case "validators": - fmt.Println(rs.Validators) + case "voters": + fmt.Println(rs.Voters) case "proposal": fmt.Println(rs.Proposal) case "proposal_block": diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 048b8bcc6..60b29c59d 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -320,9 +320,9 @@ var ( // 3 - save block and committed with truncated block store and state behind var modes = []uint{0, 1, 2, 3} -func getProposerIdx(state *State, height int64, round int32) (int32, *types.Validator) { - proposer := types.SelectProposer(state.Validators, state.state.LastProofHash, height, round) - return state.Validators.GetByAddress(proposer.PubKey.Address()) +func getProposerIdx(state *State, height int64, round int32) (int, *types.Validator) { + proposer := state.Validators.SelectProposer(state.state.LastProofHash, height, round) + return state.Voters.GetByAddress(proposer.PubKey.Address()) } func createProposalBlock(cs *State, proposerState *State, round int32) (*types.Block, *types.PartSet) { @@ -916,7 +916,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { stateDB, state, store := stateAndStore(config, pubKey, appVersion) stateStore := sm.NewStore(stateDB) genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) - state.LastValidators = state.Validators.Copy() + state.LastVoters = state.Voters.Copy() // mode = 0 for committing all the blocks blocks := makeBlocks(3, &state, privVal) store.chain = blocks @@ -1016,7 +1016,7 @@ func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.Bloc message := state.MakeHashMessage(0) proof, _ := privVal.GenerateVRFProof(message) return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, - types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address, 0, proof) + state.Validators.SelectProposer(state.LastProofHash, height, 0).Address, 0, proof) } type badApp struct { diff --git a/consensus/state.go b/consensus/state.go index e591a8760..c9607aac6 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -254,6 +254,7 @@ func (cs *State) GetRoundStateSimpleJSON() ([]byte, error) { } // GetValidators returns a copy of the current validators. +// ValidatorOrVoter: validator func (cs *State) GetValidators() (int64, []*types.Validator) { cs.mtx.RLock() defer cs.mtx.RUnlock() @@ -562,8 +563,7 @@ func (cs *State) reconstructLastCommit(state sm.State) { state.LastBlockHeight, )) } - - lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) + lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastVoters) if !lastPrecommits.HasTwoThirdsMajority() { panic("failed to reconstruct last commit; does not have +2/3 maj") } @@ -615,6 +615,7 @@ func (cs *State) updateToState(state sm.State) { // Reset fields based on state. validators := state.Validators + voters := state.Voters switch { case state.LastBlockHeight == 0: // Very first commit should be empty. @@ -659,7 +660,8 @@ func (cs *State) updateToState(state sm.State) { cs.StartTime = cs.config.Commit(cs.CommitTime) } - cs.Validators = validators + cs.Validators = state.Validators.Copy() + cs.Voters = state.Voters.Copy() cs.Proposal = nil cs.ProposalBlock = nil cs.ProposalBlockParts = nil @@ -669,11 +671,11 @@ func (cs *State) updateToState(state sm.State) { cs.ValidRound = -1 cs.ValidBlock = nil cs.ValidBlockParts = nil - cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) + cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, voters) cs.CommitRound = -1 - cs.LastValidators = state.LastValidators + cs.LastVoters = state.LastVoters cs.TriggeredTimeoutPrecommit = false - cs.Proposer = types.SelectProposer(validators, state.LastProofHash, cs.Height, cs.Round) + cs.Proposer = validators.SelectProposer(state.LastProofHash, cs.Height, cs.Round) cs.state = state @@ -976,7 +978,7 @@ func (cs *State) enterNewRound(height int64, round int32) { logger.Debug("entering new round", "current", fmt.Sprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) // Select the current height and round Proposer - cs.Proposer = types.SelectProposer(cs.Validators, cs.state.LastProofHash, height, round) + cs.Proposer = cs.Validators.SelectProposer(cs.state.LastProofHash, height, round) // Setup new round // we don't fire newStep for this step, @@ -1082,8 +1084,8 @@ func (cs *State) enterPropose(height int64, round int32) { address := cs.privValidatorPubKey.Address() // if not a validator, we're done - if !cs.Validators.HasAddress(address) { - logger.Debug("node is not a validator", "addr", address, "vals", cs.Validators) + if !cs.Voters.HasAddress(address) { + logger.Debug("node is not a validator", "addr", address, "voters", cs.Voters) return } @@ -1701,14 +1703,14 @@ func (cs *State) pruneBlocks(retainHeight int64) (uint64, error) { } func (cs *State) recordMetrics(height int64, block *types.Block) { - cs.metrics.Validators.Set(float64(cs.Validators.Size())) - cs.metrics.ValidatorsPower.Set(float64(cs.Validators.TotalVotingPower())) + cs.metrics.Voters.Set(float64(cs.Voters.Size())) + cs.metrics.VotersPower.Set(float64(cs.Voters.TotalVotingPower())) var ( - missingValidators int - missingValidatorsPower int64 + missingVoters int + missingVotersPower int64 ) - // height=0 -> MissingValidators and MissingValidatorsPower are both 0. + // height=0 -> MissingVoters and MissingVotersPower are both 0. // Remember that the first LastCommit is intentionally empty, so it's not // fair to increment missing validators number. if height > cs.state.InitialHeight { @@ -1716,12 +1718,12 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { // after first block. var ( commitSize = block.LastCommit.Size() - valSetLen = len(cs.LastValidators.Validators) + valSetLen = len(cs.LastVoters.Voters) address types.Address ) if commitSize != valSetLen { panic(fmt.Sprintf("commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", - commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastValidators.Validators)) + commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastVoters.Voters)) } if cs.privValidator != nil { @@ -1733,29 +1735,29 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { } } - for i, val := range cs.LastValidators.Validators { + for i, val := range cs.LastVoters.Voters { commitSig := block.LastCommit.Signatures[i] if commitSig.Absent() { - missingValidators++ - missingValidatorsPower += val.VotingPower + missingVoters++ + missingVotersPower += val.VotingPower } if bytes.Equal(val.Address, address) { label := []string{ "validator_address", val.Address.String(), } - cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) + cs.metrics.VoterPower.With(label...).Set(float64(val.VotingPower)) if commitSig.ForBlock() { - cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) + cs.metrics.VoterLastSignedHeight.With(label...).Set(float64(height)) } else { - cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) + cs.metrics.VoterMissedBlocks.With(label...).Add(float64(1)) } } } } - cs.metrics.MissingValidators.Set(float64(missingValidators)) - cs.metrics.MissingValidatorsPower.Set(float64(missingValidatorsPower)) + cs.metrics.MissingVoters.Set(float64(missingVoters)) + cs.metrics.MissingVotersPower.Set(float64(missingVotersPower)) // NOTE: byzantine validators power and count is only for consensus evidence i.e. duplicate vote var ( @@ -1770,8 +1772,8 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { } } } - cs.metrics.ByzantineValidators.Set(float64(byzantineValidatorsCount)) - cs.metrics.ByzantineValidatorsPower.Set(float64(byzantineValidatorsPower)) + cs.metrics.ByzantineVoters.Set(float64(byzantineValidatorsCount)) + cs.metrics.ByzantineVotersPower.Set(float64(byzantineValidatorsPower)) if height > 1 { lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) @@ -1809,7 +1811,7 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { } // If consensus does not enterNewRound yet, cs.Proposer may be nil or prior proposer, so don't use cs.Proposer - proposer := types.SelectProposer(cs.Validators, cs.state.LastProofHash, proposal.Height, proposal.Round) + proposer := cs.Validators.SelectProposer(cs.state.LastProofHash, proposal.Height, proposal.Round) p := proposal.ToProto() // Verify signature @@ -2221,7 +2223,7 @@ func (cs *State) signAddVote(msgType tmproto.SignedMsgType, hash []byte, header } // If the node not in the validator set, do nothing. - if !cs.Validators.HasAddress(cs.privValidatorPubKey.Address()) { + if !cs.Voters.HasAddress(cs.privValidatorPubKey.Address()) { return nil } diff --git a/consensus/state_test.go b/consensus/state_test.go index 36064a1b2..1e40527f1 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -86,7 +86,7 @@ func TestStateProposerSelection0(t *testing.T) { ensureNewRound(newRoundCh, height+1, 0) prop = cs1.GetRoundState().Proposer - addr := types.SelectProposer(cs1.Validators, cs1.state.LastProofHash, cs1.Height, cs1.Round).PubKey.Address() + addr := cs1.Validators.SelectProposer(cs1.state.LastProofHash, cs1.Height, cs1.Round).PubKey.Address() if !bytes.Equal(prop.Address, addr) { panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address)) } @@ -110,10 +110,10 @@ func TestStateProposerSelection2(t *testing.T) { // everyone just votes nil. we get a new proposer each round for i := int32(0); int(i) < len(vss); i++ { prop := cs1.GetRoundState().Proposer - addr := types.SelectProposer(cs1.Validators, cs1.state.LastProofHash, height, i+round).PubKey.Address() + addr := cs1.Validators.SelectProposer(cs1.state.LastProofHash, height, i+round).PubKey.Address() correctProposer := addr if !bytes.Equal(prop.Address, correctProposer) { - idx, _ := cs1.Validators.GetByAddress(addr) + idx, _ := cs1.Voters.GetByAddress(addr) panic(fmt.Sprintf( "expected RoundState.Validators.GetProposer() to be validator %d. Got %X", idx, diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index 6a5c0b495..0711a04e2 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -39,9 +39,9 @@ We let each peer provide us with up to 2 unexpected "catchup" rounds. One for their LastCommit round, and another for the official commit round. */ type HeightVoteSet struct { - chainID string - height int64 - valSet *types.ValidatorSet + chainID string + height int64 + voterSet *types.VoterSet mtx sync.Mutex round int32 // max tracked round @@ -49,20 +49,20 @@ type HeightVoteSet struct { peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds } -func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet { +func NewHeightVoteSet(chainID string, height int64, voterSet *types.VoterSet) *HeightVoteSet { hvs := &HeightVoteSet{ chainID: chainID, } - hvs.Reset(height, valSet) + hvs.Reset(height, voterSet) return hvs } -func (hvs *HeightVoteSet) Reset(height int64, valSet *types.ValidatorSet) { +func (hvs *HeightVoteSet) Reset(height int64, voterSet *types.VoterSet) { hvs.mtx.Lock() defer hvs.mtx.Unlock() hvs.height = height - hvs.valSet = valSet + hvs.voterSet = voterSet hvs.roundVoteSets = make(map[int32]RoundVoteSet) hvs.peerCatchupRounds = make(map[p2p.ID][]int32) @@ -104,8 +104,8 @@ func (hvs *HeightVoteSet) addRound(round int32) { panic("addRound() for an existing round") } // log.Debug("addRound(round)", "round", round) - prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet) - precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet) + prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.voterSet) + precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.voterSet) hvs.roundVoteSets[round] = RoundVoteSet{ Prevotes: prevotes, Precommits: precommits, diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 68c4d98c0..86322e807 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -23,7 +23,7 @@ func TestMain(m *testing.M) { } func TestPeerCatchupRounds(t *testing.T) { - valSet, privVals := types.RandValidatorSet(10, 1) + _, valSet, privVals := types.RandVoterSet(10, 1) hvs := NewHeightVoteSet(config.ChainID(), 1, valSet) diff --git a/consensus/types/round_state.go b/consensus/types/round_state.go index a821280b0..92b3e2ddc 100644 --- a/consensus/types/round_state.go +++ b/consensus/types/round_state.go @@ -73,6 +73,7 @@ type RoundState struct { // Subjective time when +2/3 precommits for Block at Round were found CommitTime time.Time `json:"commit_time"` Validators *types.ValidatorSet `json:"validators"` + Voters *types.VoterSet `json:"voters"` Proposer *types.Validator `json:"proposer"` Proposal *types.Proposal `json:"proposal"` ProposalBlock *types.Block `json:"proposal_block"` @@ -86,12 +87,12 @@ type RoundState struct { ValidBlock *types.Block `json:"valid_block"` // Last known block of POL mentioned above. // Last known block parts of POL mentioned above. - ValidBlockParts *types.PartSet `json:"valid_block_parts"` - Votes *HeightVoteSet `json:"votes"` - CommitRound int32 `json:"commit_round"` // - LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 - LastValidators *types.ValidatorSet `json:"last_validators"` - TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` + ValidBlockParts *types.PartSet `json:"valid_block_parts"` + Votes *HeightVoteSet `json:"votes"` + CommitRound int32 `json:"commit_round"` // + LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 + LastVoters *types.VoterSet `json:"last_voters"` + TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` } // Compressed version of the RoundState for use in RPC @@ -113,7 +114,7 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple { } addr := rs.Proposer.Address - idx, _ := rs.Validators.GetByAddress(addr) + idx, _ := rs.Voters.GetByAddress(addr) return RoundStateSimple{ HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step), @@ -124,7 +125,7 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple { Votes: votesJSON, Proposer: types.ValidatorInfo{ Address: addr, - Index: idx, + Index: int32(idx), }, } } @@ -132,7 +133,7 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple { // NewRoundEvent returns the RoundState with proposer information as an event. func (rs *RoundState) NewRoundEvent() types.EventDataNewRound { addr := rs.Proposer.Address - idx, _ := rs.Validators.GetByAddress(addr) + idx, _ := rs.Voters.GetByAddress(addr) return types.EventDataNewRound{ Height: rs.Height, @@ -140,7 +141,7 @@ func (rs *RoundState) NewRoundEvent() types.EventDataNewRound { Step: rs.Step.String(), Proposer: types.ValidatorInfo{ Address: addr, - Index: idx, + Index: int32(idx), }, } } @@ -182,7 +183,7 @@ func (rs *RoundState) StringIndented(indent string) string { %s H:%v R:%v S:%v %s StartTime: %v %s CommitTime: %v -%s Validators: %v +%s Voters: %v %s Proposer: %v %s Proposal: %v %s ProposalBlock: %v %v @@ -192,12 +193,12 @@ func (rs *RoundState) StringIndented(indent string) string { %s ValidBlock: %v %v %s Votes: %v %s LastCommit: %v -%s LastValidators:%v +%s LastVoters:%v %s}`, indent, rs.Height, rs.Round, rs.Step, indent, rs.StartTime, indent, rs.CommitTime, - indent, rs.Validators.StringIndented(indent+" "), + indent, rs.Voters.StringIndented(indent+" "), indent, rs.Proposer.String(), indent, rs.Proposal, indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(), @@ -207,7 +208,7 @@ func (rs *RoundState) StringIndented(indent string) string { indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(), indent, rs.Votes.StringIndented(indent+" "), indent, rs.LastCommit.StringShort(), - indent, rs.LastValidators.StringIndented(indent+" "), + indent, rs.LastVoters.StringIndented(indent+" "), indent) } diff --git a/evidence/pool.go b/evidence/pool.go index 5b6b9ef7e..697d14277 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -527,11 +527,11 @@ func (evpool *Pool) processConsensusBuffer(state sm.State) { voteSet.VoteA, voteSet.VoteB, state.LastBlockTime, - state.LastValidators, + state.LastVoters, ) case voteSet.VoteA.Height < state.LastBlockHeight: - valSet, err := evpool.stateDB.LoadValidators(voteSet.VoteA.Height) + _, voterSet, err := evpool.stateDB.LoadValidators(voteSet.VoteA.Height) if err != nil { evpool.logger.Error("failed to load validator set for conflicting votes", "height", voteSet.VoteA.Height, "err", err, @@ -547,7 +547,7 @@ func (evpool *Pool) processConsensusBuffer(state sm.State) { voteSet.VoteA, voteSet.VoteB, blockMeta.Header.Time, - valSet, + voterSet, ) default: diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 9fb9ec9f2..4ede64b06 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -77,7 +77,7 @@ func TestEvidencePoolBasic(t *testing.T) { blockStore = &mocks.BlockStore{} ) - valSet, privVals := types.RandValidatorSet(1, 10) + valSet, _, privVals := types.RandVoterSet(1, 10) blockStore.On("LoadBlockMeta", mock.AnythingOfType("int64")).Return( &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}, @@ -201,7 +201,7 @@ func TestReportConflictingVotes(t *testing.T) { state := pool.State() state.LastBlockHeight++ state.LastBlockTime = ev.Time() - state.LastValidators = types.NewValidatorSet([]*types.Validator{val}) + state.LastVoters = types.NewVoterSet([]*types.Validator{val}) pool.Update(state, []types.Evidence{}) // should be able to retrieve evidence from pool @@ -273,15 +273,15 @@ func TestCheckEvidenceWithLightClientAttack(t *testing.T) { validatorPower int64 = 10 height int64 = 10 ) - conflictingVals, conflictingPrivVals := types.RandValidatorSet(nValidators, validatorPower) + conflictingVals, conflictingVoters, conflictingPrivVals := types.RandVoterSet(nValidators, validatorPower) trustedHeader := makeHeaderRandom(height) trustedHeader.Time = defaultEvidenceTime conflictingHeader := makeHeaderRandom(height) - conflictingHeader.ValidatorsHash = conflictingVals.Hash() + conflictingHeader.VotersHash = conflictingVals.Hash() - trustedHeader.ValidatorsHash = conflictingHeader.ValidatorsHash - trustedHeader.NextValidatorsHash = conflictingHeader.NextValidatorsHash + trustedHeader.VotersHash = conflictingHeader.VotersHash + trustedHeader.NextVotersHash = conflictingHeader.NextVotersHash trustedHeader.ConsensusHash = conflictingHeader.ConsensusHash trustedHeader.AppHash = conflictingHeader.AppHash trustedHeader.LastResultsHash = conflictingHeader.LastResultsHash @@ -289,7 +289,7 @@ func TestCheckEvidenceWithLightClientAttack(t *testing.T) { // for simplicity we are simulating a duplicate vote attack where all the validators in the // conflictingVals set voted twice blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) - voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals) + voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVoters) commit, err := types.MakeCommit(blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) ev := &types.LightClientAttackEvidence{ @@ -299,6 +299,7 @@ func TestCheckEvidenceWithLightClientAttack(t *testing.T) { Commit: commit, }, ValidatorSet: conflictingVals, + VoterSet: conflictingVoters, }, CommonHeight: 10, TotalVotingPower: int64(nValidators) * validatorPower, @@ -307,7 +308,7 @@ func TestCheckEvidenceWithLightClientAttack(t *testing.T) { } trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) - trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals) + trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVoters) trustedCommit, err := types.MakeCommit(trustedBlockID, height, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) @@ -401,7 +402,7 @@ func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) s LastBlockTime: defaultEvidenceTime, Validators: valSet, NextValidators: valSet.Copy(), - LastValidators: valSet, + LastVoters: types.ToVoterAll(valSet), LastHeightValidatorsChanged: 1, ConsensusParams: tmproto.ConsensusParams{ Block: tmproto.BlockParams{ @@ -450,7 +451,7 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.Bloc lastCommit := makeCommit(i-1, valAddr) proof := state.MakeHashMessage(round) block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil, - types.SelectProposer(state.Validators, proof, i, round).Address, round, proof) + state.Validators.SelectProposer(proof, i, round).Address, round, proof) block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute) block.Header.Version = tmversion.Consensus{Block: version.BlockProtocol, App: 1} const parts = 1 diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index e3662bea9..8441de259 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -375,7 +375,7 @@ func TestEvidenceVectors(t *testing.T) { VotingPower: 10, } - valSet := types.NewValidatorSet([]*types.Validator{val}) + valSet := types.NewVoterSet([]*types.Validator{val}) dupl := types.NewDuplicateVoteEvidence( exampleVote(1), diff --git a/evidence/verify.go b/evidence/verify.go index 0721ade9a..7859b4b2d 100644 --- a/evidence/verify.go +++ b/evidence/verify.go @@ -51,18 +51,18 @@ func (evpool *Pool) verify(evidence types.Evidence) error { // apply the evidence-specific verification logic switch ev := evidence.(type) { case *types.DuplicateVoteEvidence: - valSet, err := evpool.stateDB.LoadValidators(evidence.Height()) + _, voterSet, err := evpool.stateDB.LoadValidators(evidence.Height()) if err != nil { return err } - return VerifyDuplicateVote(ev, state.ChainID, valSet) + return VerifyDuplicateVote(ev, state.ChainID, voterSet) case *types.LightClientAttackEvidence: commonHeader, err := getSignedHeader(evpool.blockStore, evidence.Height()) if err != nil { return err } - commonVals, err := evpool.stateDB.LoadValidators(evidence.Height()) + _, commonVoters, err := evpool.stateDB.LoadValidators(evidence.Height()) if err != nil { return err } @@ -75,14 +75,14 @@ func (evpool *Pool) verify(evidence types.Evidence) error { } } - err = VerifyLightClientAttack(ev, commonHeader, trustedHeader, commonVals, state.LastBlockTime, + err = VerifyLightClientAttack(ev, commonHeader, trustedHeader, commonVoters, state.LastBlockTime, state.ConsensusParams.Evidence.MaxAgeDuration) if err != nil { return err } // find out what type of attack this was and thus extract the malicious validators. Note in the case of an // Amnesia attack we don't have any malicious validators. - validators := ev.GetByzantineValidators(commonVals, trustedHeader) + validators := ev.GetByzantineValidators(commonVoters, trustedHeader) // ensure this matches the validators that are listed in the evidence. They should be ordered based on power. if validators == nil && ev.ByzantineValidators != nil { return fmt.Errorf("expected nil validators from an amnesia light client attack but got %d", @@ -121,11 +121,11 @@ func (evpool *Pool) verify(evidence types.Evidence) error { // the conflicting header's commit // - the nodes trusted header at the same height as the conflicting header has a different hash func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, trustedHeader *types.SignedHeader, - commonVals *types.ValidatorSet, now time.Time, trustPeriod time.Duration) error { + commonVals *types.VoterSet, now time.Time, trustPeriod time.Duration) error { // In the case of lunatic attack we need to perform a single verification jump between the // common header and the conflicting one if commonHeader.Height != trustedHeader.Height { - err := light.Verify(commonHeader, commonVals, e.ConflictingBlock.SignedHeader, e.ConflictingBlock.ValidatorSet, + err := light.Verify(commonHeader, commonVals, e.ConflictingBlock.SignedHeader, e.ConflictingBlock.VoterSet, trustPeriod, now, 0*time.Second, light.DefaultTrustLevel) if err != nil { return fmt.Errorf("skipping verification from common to conflicting header failed: %w", err) @@ -137,7 +137,7 @@ func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, t " block to be correctly derived yet it wasn't") } // ensure that 2/3 of the validator set did vote for this block - if err := e.ConflictingBlock.ValidatorSet.VerifyCommitLight(trustedHeader.ChainID, e.ConflictingBlock.Commit.BlockID, + if err := e.ConflictingBlock.VoterSet.VerifyCommitLight(trustedHeader.ChainID, e.ConflictingBlock.Commit.BlockID, e.ConflictingBlock.Height, e.ConflictingBlock.Commit); err != nil { return fmt.Errorf("invalid commit from conflicting block: %w", err) } @@ -162,7 +162,7 @@ func VerifyLightClientAttack(e *types.LightClientAttackEvidence, commonHeader, t // - the height, round, type and validator address of the votes must be the same // - the block ID's must be different // - The signatures must both be valid -func VerifyDuplicateVote(e *types.DuplicateVoteEvidence, chainID string, valSet *types.ValidatorSet) error { +func VerifyDuplicateVote(e *types.DuplicateVoteEvidence, chainID string, valSet *types.VoterSet) error { _, val := valSet.GetByAddress(e.VoteA.ValidatorAddress) if val == nil { return fmt.Errorf("address %X was not a validator at height %d", e.VoteA.ValidatorAddress, e.Height()) @@ -244,8 +244,8 @@ func getSignedHeader(blockStore BlockStore, height int64) (*types.SignedHeader, // or not. If it is then all the deterministic fields of the header should be the same. // If not, it is an invalid header and constitutes a lunatic attack. func isInvalidHeader(trusted, conflicting *types.Header) bool { - return !bytes.Equal(trusted.ValidatorsHash, conflicting.ValidatorsHash) || - !bytes.Equal(trusted.NextValidatorsHash, conflicting.NextValidatorsHash) || + return !bytes.Equal(trusted.VotersHash, conflicting.VotersHash) || + !bytes.Equal(trusted.NextVotersHash, conflicting.NextVotersHash) || !bytes.Equal(trusted.ConsensusHash, conflicting.ConsensusHash) || !bytes.Equal(trusted.AppHash, conflicting.AppHash) || !bytes.Equal(trusted.LastResultsHash, conflicting.LastResultsHash) diff --git a/evidence/verify_test.go b/evidence/verify_test.go index 0e72582b2..99295ca54 100644 --- a/evidence/verify_test.go +++ b/evidence/verify_test.go @@ -23,12 +23,14 @@ import ( ) func TestVerifyLightClientAttack_Lunatic(t *testing.T) { - commonVals, commonPrivVals := types.RandValidatorSet(2, 10) + commonVals, commonVoters, commonPrivVals := types.RandVoterSet(2, 10) newVal, newPrivVal := types.RandValidator(false, 9) conflictingVals, err := types.ValidatorSetFromExistingValidators(append(commonVals.Validators, newVal)) require.NoError(t, err) + conflictingVoters, err := types.ValidatorSetFromExistingValidators(append(commonVoters.Voters, newVal)) + conflictingVoterSet := types.ToVoterAll(conflictingVoters) conflictingPrivVals := append(commonPrivVals, newPrivVal) commonHeader := makeHeaderRandom(4) @@ -37,11 +39,11 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { conflictingHeader := makeHeaderRandom(10) conflictingHeader.Time = defaultEvidenceTime.Add(1 * time.Hour) - conflictingHeader.ValidatorsHash = conflictingVals.Hash() + conflictingHeader.VotersHash = conflictingVoterSet.Hash() // we are simulating a lunatic light client attack blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) - voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) + voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVoterSet) commit, err := types.MakeCommit(blockID, 10, 1, voteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) ev := &types.LightClientAttackEvidence{ @@ -51,6 +53,7 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { Commit: commit, }, ValidatorSet: conflictingVals, + VoterSet: conflictingVoterSet, }, CommonHeight: 4, TotalVotingPower: 20, @@ -63,8 +66,8 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { Commit: &types.Commit{}, } trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) - vals, privVals := types.RandValidatorSet(3, 8) - trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), vals) + _, voters, privVals := types.RandVoterSet(3, 8) + trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), voters) trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, privVals, defaultEvidenceTime) require.NoError(t, err) trustedSignedHeader := &types.SignedHeader{ @@ -73,18 +76,18 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { } // good pass -> no error - err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVals, + err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVoters, defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour) assert.NoError(t, err) // trusted and conflicting hashes are the same -> an error should be returned - err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, ev.ConflictingBlock.SignedHeader, commonVals, + err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, ev.ConflictingBlock.SignedHeader, commonVoters, defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour) assert.Error(t, err) // evidence with different total validator power should fail ev.TotalVotingPower = 1 - err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVals, + err = evidence.VerifyLightClientAttack(ev, commonSignedHeader, trustedSignedHeader, commonVoters, defaultEvidenceTime.Add(2*time.Hour), 3*time.Hour) assert.Error(t, err) ev.TotalVotingPower = 20 @@ -129,14 +132,14 @@ func TestVerifyLightClientAttack_Lunatic(t *testing.T) { } func TestVerifyLightClientAttack_Equivocation(t *testing.T) { - conflictingVals, conflictingPrivVals := types.RandValidatorSet(5, 10) + conflictingVals, conflictingVoters, conflictingPrivVals := types.RandVoterSet(5, 10) trustedHeader := makeHeaderRandom(10) conflictingHeader := makeHeaderRandom(10) - conflictingHeader.ValidatorsHash = conflictingVals.Hash() + conflictingHeader.VotersHash = conflictingVoters.Hash() - trustedHeader.ValidatorsHash = conflictingHeader.ValidatorsHash - trustedHeader.NextValidatorsHash = conflictingHeader.NextValidatorsHash + trustedHeader.VotersHash = conflictingHeader.VotersHash + trustedHeader.NextVotersHash = conflictingHeader.NextVotersHash trustedHeader.ConsensusHash = conflictingHeader.ConsensusHash trustedHeader.AppHash = conflictingHeader.AppHash trustedHeader.LastResultsHash = conflictingHeader.LastResultsHash @@ -144,7 +147,7 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { // we are simulating a duplicate vote attack where all the validators in the conflictingVals set // except the last validator vote twice blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) - voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) + voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVoters) commit, err := types.MakeCommit(blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime) require.NoError(t, err) ev := &types.LightClientAttackEvidence{ @@ -154,6 +157,7 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { Commit: commit, }, ValidatorSet: conflictingVals, + VoterSet: conflictingVoters, }, CommonHeight: 10, ByzantineValidators: conflictingVals.Validators[:4], @@ -162,7 +166,7 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { } trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) - trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) + trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVoters) trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) trustedSignedHeader := &types.SignedHeader{ @@ -171,23 +175,23 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { } // good pass -> no error - err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVals, + err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVoters, defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour) assert.NoError(t, err) // trusted and conflicting hashes are the same -> an error should be returned - err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVals, + err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVoters, defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour) assert.Error(t, err) // conflicting header has different next validators hash which should have been correctly derived from // the previous round - ev.ConflictingBlock.Header.NextValidatorsHash = crypto.CRandBytes(tmhash.Size) + ev.ConflictingBlock.Header.NextVotersHash = crypto.CRandBytes(tmhash.Size) err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, nil, defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour) assert.Error(t, err) // revert next validators hash - ev.ConflictingBlock.Header.NextValidatorsHash = trustedHeader.NextValidatorsHash + ev.ConflictingBlock.Header.NextVotersHash = trustedHeader.NextVotersHash state := sm.State{ LastBlockTime: defaultEvidenceTime.Add(1 * time.Minute), @@ -214,13 +218,13 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) { } func TestVerifyLightClientAttack_Amnesia(t *testing.T) { - conflictingVals, conflictingPrivVals := types.RandValidatorSet(5, 10) + conflictingVals, conflictingVoters, conflictingPrivVals := types.RandVoterSet(5, 10) conflictingHeader := makeHeaderRandom(10) - conflictingHeader.ValidatorsHash = conflictingVals.Hash() + conflictingHeader.VotersHash = conflictingVoters.Hash() trustedHeader := makeHeaderRandom(10) - trustedHeader.ValidatorsHash = conflictingHeader.ValidatorsHash - trustedHeader.NextValidatorsHash = conflictingHeader.NextValidatorsHash + trustedHeader.VotersHash = conflictingHeader.VotersHash + trustedHeader.NextVotersHash = conflictingHeader.NextVotersHash trustedHeader.AppHash = conflictingHeader.AppHash trustedHeader.ConsensusHash = conflictingHeader.ConsensusHash trustedHeader.LastResultsHash = conflictingHeader.LastResultsHash @@ -228,7 +232,7 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { // we are simulating an amnesia attack where all the validators in the conflictingVals set // except the last validator vote twice. However this time the commits are of different rounds. blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash")) - voteSet := types.NewVoteSet(evidenceChainID, 10, 0, tmproto.SignedMsgType(2), conflictingVals) + voteSet := types.NewVoteSet(evidenceChainID, 10, 0, tmproto.SignedMsgType(2), conflictingVoters) commit, err := types.MakeCommit(blockID, 10, 0, voteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) ev := &types.LightClientAttackEvidence{ @@ -238,6 +242,7 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { Commit: commit, }, ValidatorSet: conflictingVals, + VoterSet: conflictingVoters, }, CommonHeight: 10, ByzantineValidators: nil, // with amnesia evidence no validators are submitted as abci evidence @@ -246,7 +251,7 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { } trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash")) - trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals) + trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVoters) trustedCommit, err := types.MakeCommit(trustedBlockID, 10, 1, trustedVoteSet, conflictingPrivVals, defaultEvidenceTime) require.NoError(t, err) trustedSignedHeader := &types.SignedHeader{ @@ -255,12 +260,12 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) { } // good pass -> no error - err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVals, + err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, trustedSignedHeader, conflictingVoters, defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour) assert.NoError(t, err) // trusted and conflicting hashes are the same -> an error should be returned - err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVals, + err = evidence.VerifyLightClientAttack(ev, trustedSignedHeader, ev.ConflictingBlock.SignedHeader, conflictingVoters, defaultEvidenceTime.Add(1*time.Minute), 2*time.Hour) assert.Error(t, err) @@ -297,7 +302,7 @@ type voteData struct { func TestVerifyDuplicateVoteEvidence(t *testing.T) { val := types.NewMockPV() val2 := types.NewMockPV() - valSet := types.NewValidatorSet([]*types.Validator{val.ExtractIntoValidator(1)}) + valSet := types.NewVoterSet([]*types.Validator{val.ExtractIntoValidator(1)}) blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) @@ -413,20 +418,20 @@ func makeVote( func makeHeaderRandom(height int64) *types.Header { return &types.Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, - ChainID: evidenceChainID, - Height: height, - Time: defaultEvidenceTime, - LastBlockID: makeBlockID([]byte("headerhash"), 1000, []byte("partshash")), - LastCommitHash: crypto.CRandBytes(tmhash.Size), - DataHash: crypto.CRandBytes(tmhash.Size), - ValidatorsHash: crypto.CRandBytes(tmhash.Size), - NextValidatorsHash: crypto.CRandBytes(tmhash.Size), - ConsensusHash: crypto.CRandBytes(tmhash.Size), - AppHash: crypto.CRandBytes(tmhash.Size), - LastResultsHash: crypto.CRandBytes(tmhash.Size), - EvidenceHash: crypto.CRandBytes(tmhash.Size), - ProposerAddress: crypto.CRandBytes(crypto.AddressSize), + Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, + ChainID: evidenceChainID, + Height: height, + Time: defaultEvidenceTime, + LastBlockID: makeBlockID([]byte("headerhash"), 1000, []byte("partshash")), + LastCommitHash: crypto.CRandBytes(tmhash.Size), + DataHash: crypto.CRandBytes(tmhash.Size), + VotersHash: crypto.CRandBytes(tmhash.Size), + NextVotersHash: crypto.CRandBytes(tmhash.Size), + ConsensusHash: crypto.CRandBytes(tmhash.Size), + AppHash: crypto.CRandBytes(tmhash.Size), + LastResultsHash: crypto.CRandBytes(tmhash.Size), + EvidenceHash: crypto.CRandBytes(tmhash.Size), + ProposerAddress: crypto.CRandBytes(crypto.AddressSize), } } diff --git a/libs/rand/sampling.go b/libs/rand/sampling.go index 8ba7b4518..8aaf411c7 100644 --- a/libs/rand/sampling.go +++ b/libs/rand/sampling.go @@ -9,6 +9,7 @@ import ( type Candidate interface { Priority() uint64 LessThan(other Candidate) bool + IncreaseWin() } const uint64Mask = uint64(0x7FFFFFFFFFFFFFFF) @@ -65,6 +66,55 @@ func RandomSamplingWithPriority( totalPriority, actualTotalPriority, seed, sampleSize, undrawn, undrawn, thresholds[undrawn], len(candidates))) } +const MaxSamplingLoopTry = 1000 + +// `RandomSamplingToMax` elects voters among candidates so it updates wins of candidates +// Voters can be elected by a maximum `limitCandidates`. +// However, if the likely candidates are less than the `limitCandidates`, +// the number of voters may be less than the `limitCandidates`. +// This is to prevent falling into an infinite loop. +func RandomSamplingToMax( + seed uint64, candidates []Candidate, limitCandidates int, totalPriority uint64) uint64 { + + if len(candidates) < limitCandidates { + panic("The number of candidates cannot be less limitCandidate") + } + + candidates = sort(candidates) + totalSampling := uint64(0) + winCandidates := make(map[Candidate]bool) + for len(winCandidates) < limitCandidates && totalSampling < MaxSamplingLoopTry { + threshold := uint64(float64(nextRandom(&seed)&uint64Mask) / float64(uint64Mask+1) * float64(totalPriority)) + cumulativePriority := uint64(0) + found := false + for _, candidate := range candidates { + if threshold < cumulativePriority+candidate.Priority() { + if !winCandidates[candidate] { + winCandidates[candidate] = true + } + candidate.IncreaseWin() + totalSampling++ + found = true + break + } + cumulativePriority += candidate.Priority() + } + + if !found { + panic(fmt.Sprintf("Cannot find random sample. totalPriority may be wrong: totalPriority=%d, "+ + "actualTotalPriority=%d, threshold=%d", totalPriority, sumTotalPriority(candidates), threshold)) + } + } + return totalSampling +} + +func sumTotalPriority(candidates []Candidate) (sum uint64) { + for _, candi := range candidates { + sum += candi.Priority() + } + return +} + // SplitMix64 // http://xoshiro.di.unimi.it/splitmix64.c // diff --git a/libs/rand/sampling_test.go b/libs/rand/sampling_test.go index b090f75ab..3ed78e0dc 100644 --- a/libs/rand/sampling_test.go +++ b/libs/rand/sampling_test.go @@ -9,6 +9,7 @@ import ( type Element struct { ID uint32 + Win uint64 Weight uint64 } @@ -24,6 +25,10 @@ func (e *Element) LessThan(other Candidate) bool { return e.ID < o.ID } +func (e *Element) IncreaseWin() { + e.Win++ +} + func TestRandomSamplingWithPriority(t *testing.T) { candidates := newCandidates(100, func(i int) uint64 { return uint64(i) }) @@ -84,10 +89,70 @@ func TestRandomSamplingPanicCase(t *testing.T) { } } +func numberOfWinnersAndWins(candidate []Candidate) (winners uint64, totalWins uint64) { + for _, c := range candidate { + if c.(*Element).Win > 0 { + winners++ + totalWins += c.(*Element).Win + } + } + return +} + +func TestRandomSamplingToMax(t *testing.T) { + candidates1 := newCandidates(100, func(i int) uint64 { return uint64(i) }) + voters1 := RandomSamplingToMax(0, candidates1, 10, sumTotalPriority(candidates1)) + winners, totalWins := numberOfWinnersAndWins(candidates1) + if winners != 10 { + t.Errorf(fmt.Sprintf("unexpected sample size: %d", winners)) + } + if voters1 != totalWins { + t.Errorf(fmt.Sprintf("unexpected totalWins: %d", voters1)) + } + + candidates2 := newCandidates(100, func(i int) uint64 { return uint64(i) }) + _ = RandomSamplingToMax(0, candidates2, 10, sumTotalPriority(candidates2)) + + if !sameCandidates(candidates1, candidates2) { + t.Error("The two voter sets elected by the same seed are different.") + } + + candidates3 := newCandidates(0, func(i int) uint64 { return uint64(i) }) + voters3 := RandomSamplingToMax(0, candidates3, 0, sumTotalPriority(candidates3)) + if voters3 != 0 { + t.Errorf(fmt.Sprintf("unexpected totalWins: %d", voters3)) + } +} + +func TestRandomSamplingToMaxPanic(t *testing.T) { + type Case struct { + Candidates []Candidate + TotalPriority uint64 + } + + cases := [...]*Case{ + // specified total priority is greater than actual one + {newCandidates(10, func(i int) uint64 { return 1 }), 50000}, + // limitCandidates is greater than the number of candidates + {newCandidates(5, func(i int) uint64 { return 10 }), 5}, + } + + for i, c := range cases { + func() { + defer func() { + if recover() == nil { + t.Errorf("expected panic didn't happen in case %d", i+1) + } + }() + RandomSamplingToMax(0, c.Candidates, 10, c.TotalPriority) + }() + } +} + func newCandidates(length int, prio func(int) uint64) (candidates []Candidate) { candidates = make([]Candidate, length) for i := 0; i < length; i++ { - candidates[i] = &Element{uint32(i), prio(i)} + candidates[i] = &Element{uint32(i), 0, prio(i)} } return } @@ -102,6 +167,9 @@ func sameCandidates(c1 []Candidate, c2 []Candidate) bool { if c1[i].(*Element).ID != c2[i].(*Element).ID { return false } + if c1[i].(*Element).Win != c2[i].(*Element).Win { + return false + } } return true } diff --git a/light/client.go b/light/client.go index c758a2a6b..a31cbc02e 100644 --- a/light/client.go +++ b/light/client.go @@ -358,7 +358,7 @@ func (c *Client) initializeWithTrustOptions(ctx context.Context, options TrustOp } // 2) Ensure that +2/3 of validators signed correctly. - err = l.ValidatorSet.VerifyCommitLight(c.chainID, l.Commit.BlockID, l.Height, l.Commit) + err = l.VoterSet.VerifyCommitLight(c.chainID, l.Commit.BlockID, l.Height, l.Commit) if err != nil { return fmt.Errorf("invalid commit: %w", err) } @@ -620,7 +620,7 @@ func (c *Client) verifySequential( "newHeight", interimBlock.Height, "newHash", interimBlock.Hash()) - err = VerifyAdjacent(verifiedBlock.SignedHeader, interimBlock.SignedHeader, interimBlock.ValidatorSet, + err = VerifyAdjacent(verifiedBlock.SignedHeader, interimBlock.SignedHeader, interimBlock.VoterSet, c.trustingPeriod, now, c.maxClockDrift) if err != nil { err := ErrVerificationFailed{From: verifiedBlock.Height, To: interimBlock.Height, Reason: err} @@ -711,8 +711,8 @@ func (c *Client) verifySkipping( "newHeight", blockCache[depth].Height, "newHash", blockCache[depth].Hash()) - err := Verify(verifiedBlock.SignedHeader, verifiedBlock.ValidatorSet, blockCache[depth].SignedHeader, - blockCache[depth].ValidatorSet, c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) + err := Verify(verifiedBlock.SignedHeader, verifiedBlock.VoterSet, blockCache[depth].SignedHeader, + blockCache[depth].VoterSet, c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) switch err.(type) { case nil: // Have we verified the last header diff --git a/light/client_test.go b/light/client_test.go index 49896416d..9fcf3f4fd 100644 --- a/light/client_test.go +++ b/light/client_test.go @@ -26,7 +26,7 @@ const ( var ( ctx = context.Background() keys = genPrivKeys(4) - vals = keys.ToValidators(20, 10) + vals = types.ToVoterAll(keys.ToValidators(20, 10)) bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") h1 = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) @@ -42,7 +42,7 @@ var ( Height: 1, Hash: h1.Hash(), } - valSet = map[int64]*types.ValidatorSet{ + valSet = map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: vals, @@ -55,8 +55,8 @@ var ( // last header (3/3 signed) 3: h3, } - l1 = &types.LightBlock{SignedHeader: h1, ValidatorSet: vals} - l2 = &types.LightBlock{SignedHeader: h2, ValidatorSet: vals} + l1 = &types.LightBlock{SignedHeader: h1, VoterSet: vals} + l2 = &types.LightBlock{SignedHeader: h2, VoterSet: vals} fullNode = mockp.New( chainID, headerSet, @@ -119,13 +119,13 @@ func TestMock(t *testing.T) { func TestClient_SequentialVerification(t *testing.T) { newKeys := genPrivKeys(4) - newVals := newKeys.ToValidators(10, 1) - differentVals, _ := types.RandValidatorSet(10, 100) + newVals := types.ToVoterAll(newKeys.ToValidators(10, 1)) + _, differentVoters, _ := types.RandVoterSet(10, 100) testCases := []struct { name string otherHeaders map[int64]*types.SignedHeader // all except ^ - vals map[int64]*types.ValidatorSet + vals map[int64]*types.VoterSet initErr bool verifyErr bool }{ @@ -143,7 +143,7 @@ func TestClient_SequentialVerification(t *testing.T) { 1: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, }, true, @@ -152,8 +152,8 @@ func TestClient_SequentialVerification(t *testing.T) { { "bad: no first signed header", map[int64]*types.SignedHeader{}, - map[int64]*types.ValidatorSet{ - 1: differentVals, + map[int64]*types.VoterSet{ + 1: differentVoters, }, true, true, @@ -163,8 +163,8 @@ func TestClient_SequentialVerification(t *testing.T) { map[int64]*types.SignedHeader{ 1: h1, }, - map[int64]*types.ValidatorSet{ - 1: differentVals, + map[int64]*types.VoterSet{ + 1: differentVoters, }, true, true, @@ -204,7 +204,7 @@ func TestClient_SequentialVerification(t *testing.T) { { "bad: different validator set at height 3", headerSet, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: newVals, @@ -256,16 +256,16 @@ func TestClient_SequentialVerification(t *testing.T) { func TestClient_SkippingVerification(t *testing.T) { // required for 2nd test case newKeys := genPrivKeys(4) - newVals := newKeys.ToValidators(10, 1) + newVals := types.ToVoterAll(newKeys.ToValidators(10, 1)) // 1/3+ of vals, 2/3- of newVals transitKeys := keys.Extend(3) - transitVals := transitKeys.ToValidators(10, 1) + transitVals := types.ToVoterAll(transitKeys.ToValidators(10, 1)) testCases := []struct { name string otherHeaders map[int64]*types.SignedHeader // all except ^ - vals map[int64]*types.ValidatorSet + vals map[int64]*types.VoterSet initErr bool verifyErr bool }{ @@ -289,7 +289,7 @@ func TestClient_SkippingVerification(t *testing.T) { 3: transitKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, transitVals, transitVals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(transitKeys)), }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: transitVals, @@ -309,7 +309,7 @@ func TestClient_SkippingVerification(t *testing.T) { 3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)), }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: newVals, @@ -329,7 +329,7 @@ func TestClient_SkippingVerification(t *testing.T) { 3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(newKeys)), }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: newVals, @@ -479,7 +479,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, l) assert.Equal(t, l.Hash(), h1.Hash()) - assert.Equal(t, l.ValidatorSet.Hash(), h1.ValidatorsHash.Bytes()) + assert.Equal(t, l.VoterSet.Hash(), h1.VotersHash.Bytes()) } // 2. options.Hash != trustedHeader.Hash @@ -656,7 +656,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) err = trustedStore.SaveLightBlock(&types.LightBlock{ SignedHeader: header2, - ValidatorSet: vals, + VoterSet: vals, }) require.NoError(t, err) @@ -917,7 +917,7 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { hash("app_hash2"), hash("cons_hash"), hash("results_hash"), len(keys), len(keys), types.BlockID{Hash: h1.Hash()}), }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, }, @@ -929,7 +929,7 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { 1: h1, 2: h2, }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, }, @@ -969,7 +969,7 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { } func TestClient_TrustedValidatorSet(t *testing.T) { - differentVals, _ := types.RandValidatorSet(10, 100) + _, differentVoters, _ := types.RandVoterSet(10, 100) badValSetNode := mockp.New( chainID, map[int64]*types.SignedHeader{ @@ -981,10 +981,10 @@ func TestClient_TrustedValidatorSet(t *testing.T) { 0, len(keys), types.BlockID{Hash: h1.Hash()}), 3: h3, }, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, - 2: differentVals, - 3: differentVals, + 2: differentVoters, + 3: differentVoters, }, ) @@ -1029,13 +1029,13 @@ func TestClientPrunesHeadersAndValidatorSets(t *testing.T) { } func TestClientEnsureValidHeadersAndValSets(t *testing.T) { - emptyValSet := &types.ValidatorSet{ - Validators: nil, + emptyValSet := &types.VoterSet{ + Voters: nil, } testCases := []struct { headers map[int64]*types.SignedHeader - vals map[int64]*types.ValidatorSet + vals map[int64]*types.VoterSet err bool }{ { @@ -1045,7 +1045,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) { }, { headerSet, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: nil, @@ -1063,7 +1063,7 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) { }, { headerSet, - map[int64]*types.ValidatorSet{ + map[int64]*types.VoterSet{ 1: vals, 2: vals, 3: emptyValSet, diff --git a/light/detector.go b/light/detector.go index b0e0a129a..2ea2cf9c0 100644 --- a/light/detector.go +++ b/light/detector.go @@ -237,12 +237,12 @@ func newLightClientAttackEvidence(conflicted, trusted, common *types.LightBlock) if ev.ConflictingHeaderIsInvalid(trusted.Header) { ev.CommonHeight = common.Height ev.Timestamp = common.Time - ev.TotalVotingPower = common.ValidatorSet.TotalVotingPower() + ev.TotalVotingPower = common.VoterSet.TotalVotingPower() } else { ev.CommonHeight = trusted.Height ev.Timestamp = trusted.Time - ev.TotalVotingPower = trusted.ValidatorSet.TotalVotingPower() + ev.TotalVotingPower = trusted.VoterSet.TotalVotingPower() } - ev.ByzantineValidators = ev.GetByzantineValidators(common.ValidatorSet, trusted.SignedHeader) + ev.ByzantineValidators = ev.GetByzantineValidators(common.VoterSet, trusted.SignedHeader) return ev } diff --git a/light/detector_test.go b/light/detector_test.go index 4788759e0..770f8c7ed 100644 --- a/light/detector_test.go +++ b/light/detector_test.go @@ -25,24 +25,28 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) { divergenceHeight = int64(6) primaryHeaders = make(map[int64]*types.SignedHeader, latestHeight) primaryValidators = make(map[int64]*types.ValidatorSet, latestHeight) + primaryVoters = make(map[int64]*types.VoterSet, latestHeight) ) - witnessHeaders, witnessValidators, chainKeys := genMockNodeWithKeys(chainID, latestHeight, valSize, 2, bTime) - witness := mockp.New(chainID, witnessHeaders, witnessValidators) + witnessHeaders, witnessValidators, witnessVoters, chainKeys := genMockNodeWithKeys(chainID, latestHeight, valSize, 2, bTime) + witness := mockp.New(chainID, witnessHeaders, witnessVoters) forgedKeys := chainKeys[divergenceHeight-1].ChangeKeys(3) // we change 3 out of the 5 validators (still 2/5 remain) forgedVals := forgedKeys.ToValidators(2, 0) + forgedVoters := types.ToVoterAll(forgedVals) for height := int64(1); height <= latestHeight; height++ { if height < divergenceHeight { primaryHeaders[height] = witnessHeaders[height] primaryValidators[height] = witnessValidators[height] + primaryVoters[height] = witnessVoters[height] continue } primaryHeaders[height] = forgedKeys.GenSignedHeader(chainID, height, bTime.Add(time.Duration(height)*time.Minute), - nil, forgedVals, forgedVals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(forgedKeys)) + nil, forgedVoters, forgedVoters, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(forgedKeys)) primaryValidators[height] = forgedVals + primaryVoters[height] = forgedVoters } - primary := mockp.New(chainID, primaryHeaders, primaryValidators) + primary := mockp.New(chainID, primaryHeaders, primaryVoters) c, err := light.NewClient( ctx, @@ -72,6 +76,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) { ConflictingBlock: &types.LightBlock{ SignedHeader: primaryHeaders[10], ValidatorSet: primaryValidators[10], + VoterSet: primaryVoters[10], }, CommonHeight: 4, } @@ -83,6 +88,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) { ConflictingBlock: &types.LightBlock{ SignedHeader: witnessHeaders[7], ValidatorSet: witnessValidators[7], + VoterSet: witnessVoters[7], }, CommonHeight: 4, } @@ -105,26 +111,29 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) { divergenceHeight = int64(6) primaryHeaders = make(map[int64]*types.SignedHeader, latestHeight) primaryValidators = make(map[int64]*types.ValidatorSet, latestHeight) + primaryVoters = make(map[int64]*types.VoterSet, latestHeight) ) // validators don't change in this network (however we still use a map just for convenience) - witnessHeaders, witnessValidators, chainKeys := genMockNodeWithKeys(chainID, latestHeight+2, valSize, 2, bTime) - witness := mockp.New(chainID, witnessHeaders, witnessValidators) + witnessHeaders, witnessValidators, witnessVoters, chainKeys := genMockNodeWithKeys(chainID, latestHeight+2, valSize, 2, bTime) + witness := mockp.New(chainID, witnessHeaders, witnessVoters) for height := int64(1); height <= latestHeight; height++ { if height < divergenceHeight { primaryHeaders[height] = witnessHeaders[height] primaryValidators[height] = witnessValidators[height] + primaryVoters[height] = witnessVoters[height] continue } // we don't have a network partition so we will make 4/5 (greater than 2/3) malicious and vote again for // a different block (which we do by adding txs) primaryHeaders[height] = chainKeys[height].GenSignedHeader(chainID, height, bTime.Add(time.Duration(height)*time.Minute), []types.Tx{[]byte("abcd")}, - witnessValidators[height], witnessValidators[height+1], hash("app_hash"), + witnessVoters[height], witnessVoters[height+1], hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(chainKeys[height])-1) primaryValidators[height] = witnessValidators[height] + primaryVoters[height] = witnessVoters[height] } - primary := mockp.New(chainID, primaryHeaders, primaryValidators) + primary := mockp.New(chainID, primaryHeaders, primaryVoters) c, err := light.NewClient( ctx, @@ -156,6 +165,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) { ConflictingBlock: &types.LightBlock{ SignedHeader: primaryHeaders[divergenceHeight], ValidatorSet: primaryValidators[divergenceHeight], + VoterSet: primaryVoters[divergenceHeight], }, CommonHeight: divergenceHeight, } @@ -165,6 +175,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) { ConflictingBlock: &types.LightBlock{ SignedHeader: witnessHeaders[divergenceHeight], ValidatorSet: witnessValidators[divergenceHeight], + VoterSet: primaryVoters[divergenceHeight], }, CommonHeight: divergenceHeight, } diff --git a/light/helpers_test.go b/light/helpers_test.go index 980ded956..1b40f7959 100644 --- a/light/helpers_test.go +++ b/light/helpers_test.go @@ -73,7 +73,7 @@ func (pkz privKeys) ToValidators(init, inc int64) *types.ValidatorSet { } // signHeader properly signs the header with all keys from first to last exclusive. -func (pkz privKeys) signHeader(header *types.Header, valSet *types.ValidatorSet, first, last int) *types.Commit { +func (pkz privKeys) signHeader(header *types.Header, valSet *types.VoterSet, first, last int) *types.Commit { commitSigs := make([]types.CommitSig, len(pkz)) for i := 0; i < len(pkz); i++ { commitSigs[i] = types.NewCommitSigAbsent() @@ -93,14 +93,14 @@ func (pkz privKeys) signHeader(header *types.Header, valSet *types.ValidatorSet, return types.NewCommit(header.Height, 1, blockID, commitSigs) } -func makeVote(header *types.Header, valset *types.ValidatorSet, +func makeVote(header *types.Header, valset *types.VoterSet, key crypto.PrivKey, blockID types.BlockID) *types.Vote { addr := key.PubKey().Address() idx, _ := valset.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, - ValidatorIndex: idx, + ValidatorIndex: int32(idx), Height: header.Height, Round: 1, Timestamp: tmtime.Now(), @@ -122,7 +122,7 @@ func makeVote(header *types.Header, valset *types.ValidatorSet, } func genHeader(chainID string, height int64, bTime time.Time, txs types.Txs, - valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte) *types.Header { + valset, nextValset *types.VoterSet, appHash, consHash, resHash []byte) *types.Header { return &types.Header{ Version: tmversion.Consensus{Block: version.BlockProtocol, App: 0}, @@ -131,19 +131,19 @@ func genHeader(chainID string, height int64, bTime time.Time, txs types.Txs, Time: bTime, // LastBlockID // LastCommitHash - ValidatorsHash: valset.Hash(), - NextValidatorsHash: nextValset.Hash(), - DataHash: txs.Hash(), - AppHash: appHash, - ConsensusHash: consHash, - LastResultsHash: resHash, - ProposerAddress: valset.Validators[0].Address, + VotersHash: valset.Hash(), + NextVotersHash: nextValset.Hash(), + DataHash: txs.Hash(), + AppHash: appHash, + ConsensusHash: consHash, + LastResultsHash: resHash, + ProposerAddress: valset.Voters[0].Address, } } // GenSignedHeader calls genHeader and signHeader and combines them into a SignedHeader. func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Time, txs types.Txs, - valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) *types.SignedHeader { + valset, nextValset *types.VoterSet, appHash, consHash, resHash []byte, first, last int) *types.SignedHeader { header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash) return &types.SignedHeader{ @@ -154,7 +154,7 @@ func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Tim // GenSignedHeaderLastBlockID calls genHeader and signHeader and combines them into a SignedHeader. func (pkz privKeys) GenSignedHeaderLastBlockID(chainID string, height int64, bTime time.Time, txs types.Txs, - valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int, + valset, nextValset *types.VoterSet, appHash, consHash, resHash []byte, first, last int, lastBlockID types.BlockID) *types.SignedHeader { header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash) @@ -181,11 +181,13 @@ func genMockNodeWithKeys( bTime time.Time) ( map[int64]*types.SignedHeader, map[int64]*types.ValidatorSet, + map[int64]*types.VoterSet, map[int64]privKeys) { var ( headers = make(map[int64]*types.SignedHeader, blockSize) valset = make(map[int64]*types.ValidatorSet, blockSize+1) + voterset = make(map[int64]*types.VoterSet, blockSize+1) keymap = make(map[int64]privKeys, blockSize+1) keys = genPrivKeys(valSize) totalVariation = valVariation @@ -201,11 +203,12 @@ func genMockNodeWithKeys( // genesis header and vals lastHeader := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Minute), nil, - keys.ToValidators(2, 0), newKeys.ToValidators(2, 0), hash("app_hash"), hash("cons_hash"), - hash("results_hash"), 0, len(keys)) + types.ToVoterAll(keys.ToValidators(2, 0)), types.ToVoterAll(newKeys.ToValidators(2, 0)), + hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) currentHeader := lastHeader headers[1] = currentHeader valset[1] = keys.ToValidators(2, 0) + voterset[1] = types.ToVoterAll(valset[1]) keys = newKeys for height := int64(2); height <= blockSize; height++ { @@ -215,16 +218,17 @@ func genMockNodeWithKeys( newKeys = keys.ChangeKeys(valVariationInt) currentHeader = keys.GenSignedHeaderLastBlockID(chainID, height, bTime.Add(time.Duration(height)*time.Minute), nil, - keys.ToValidators(2, 0), newKeys.ToValidators(2, 0), hash("app_hash"), hash("cons_hash"), - hash("results_hash"), 0, len(keys), types.BlockID{Hash: lastHeader.Hash()}) + types.ToVoterAll(keys.ToValidators(2, 0)), types.ToVoterAll(newKeys.ToValidators(2, 0)), + hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys), types.BlockID{Hash: lastHeader.Hash()}) headers[height] = currentHeader valset[height] = keys.ToValidators(2, 0) + voterset[height] = types.ToVoterAll(valset[height]) lastHeader = currentHeader keys = newKeys keymap[height+1] = keys } - return headers, valset, keymap + return headers, valset, voterset, keymap } func genMockNode( @@ -235,9 +239,9 @@ func genMockNode( bTime time.Time) ( string, map[int64]*types.SignedHeader, - map[int64]*types.ValidatorSet) { - headers, valset, _ := genMockNodeWithKeys(chainID, blockSize, valSize, valVariation, bTime) - return chainID, headers, valset + map[int64]*types.VoterSet) { + headers, _, voterset, _ := genMockNodeWithKeys(chainID, blockSize, valSize, valVariation, bTime) + return chainID, headers, voterset } func hash(s string) []byte { diff --git a/light/provider/http/http.go b/light/provider/http/http.go index c76e7abbb..fb268dd71 100644 --- a/light/provider/http/http.go +++ b/light/provider/http/http.go @@ -75,14 +75,15 @@ func (p *http) LightBlock(ctx context.Context, height int64) (*types.LightBlock, return nil, err } - vs, err := p.validatorSet(ctx, &sh.Height) + valSet, voterSet, err := p.voterSet(ctx, &sh.Height) if err != nil { return nil, err } lb := &types.LightBlock{ SignedHeader: sh, - ValidatorSet: vs, + ValidatorSet: valSet, + VoterSet: voterSet, } err = lb.ValidateBasic(p.chainID) @@ -99,7 +100,7 @@ func (p *http) ReportEvidence(ctx context.Context, ev types.Evidence) error { return err } -func (p *http) validatorSet(ctx context.Context, height *int64) (*types.ValidatorSet, error) { +func (p *http) voterSet(ctx context.Context, height *int64) (*types.ValidatorSet, *types.VoterSet, error) { // Since the malicious node could report a massive number of pages, making us // spend a considerable time iterating, we restrict the number of pages here. // => 10000 validators max @@ -108,21 +109,22 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato var ( perPage = 100 vals = []*types.Validator{} + voters = []*types.Validator{} page = 1 total = -1 ) for len(vals) != total && page <= maxPages { for attempt := 1; attempt <= maxRetryAttempts; attempt++ { - res, err := p.client.Validators(ctx, height, &page, &perPage) + res, err := p.client.Voters(ctx, height, &page, &perPage) if err != nil { // TODO: standardize errors on the RPC side if regexpMissingHeight.MatchString(err.Error()) { - return nil, provider.ErrLightBlockNotFound + return nil, nil, provider.ErrLightBlockNotFound } // if we have exceeded retry attempts then return no response error if attempt == maxRetryAttempts { - return nil, provider.ErrNoResponse + return nil, nil, provider.ErrNoResponse } // else we wait and try again with exponential backoff time.Sleep(backoffTimeout(uint16(attempt))) @@ -131,13 +133,13 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato // Validate response. if len(res.Validators) == 0 { - return nil, provider.ErrBadLightBlock{ + return nil, nil, provider.ErrBadLightBlock{ Reason: fmt.Errorf("validator set is empty (height: %d, page: %d, per_page: %d)", height, page, perPage), } } if res.Total <= 0 { - return nil, provider.ErrBadLightBlock{ + return nil, nil, provider.ErrBadLightBlock{ Reason: fmt.Errorf("total number of vals is <= 0: %d (height: %d, page: %d, per_page: %d)", res.Total, height, page, perPage), } @@ -145,6 +147,9 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato total = res.Total vals = append(vals, res.Validators...) + for _, index := range res.VoterIndices { + voters = append(voters, res.Validators[index]) + } page++ break } @@ -152,9 +157,13 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato valSet, err := types.ValidatorSetFromExistingValidators(vals) if err != nil { - return nil, provider.ErrBadLightBlock{Reason: err} + return nil, nil, provider.ErrBadLightBlock{Reason: err} + } + voterSet, err := types.ValidatorSetFromExistingValidators(voters) + if err != nil { + return nil, nil, provider.ErrBadLightBlock{Reason: err} } - return valSet, nil + return valSet, types.ToVoterAll(voterSet), nil } func (p *http) signedHeader(ctx context.Context, height *int64) (*types.SignedHeader, error) { diff --git a/light/provider/mock/mock.go b/light/provider/mock/mock.go index cf28846ef..ac8996277 100644 --- a/light/provider/mock/mock.go +++ b/light/provider/mock/mock.go @@ -13,7 +13,7 @@ import ( type Mock struct { chainID string headers map[int64]*types.SignedHeader - vals map[int64]*types.ValidatorSet + votes map[int64]*types.VoterSet evidenceToReport map[string]types.Evidence // hash => evidence } @@ -21,11 +21,11 @@ var _ provider.Provider = (*Mock)(nil) // New creates a mock provider with the given set of headers and validator // sets. -func New(chainID string, headers map[int64]*types.SignedHeader, vals map[int64]*types.ValidatorSet) *Mock { +func New(chainID string, headers map[int64]*types.SignedHeader, voters map[int64]*types.VoterSet) *Mock { return &Mock{ chainID: chainID, headers: headers, - vals: vals, + votes: voters, evidenceToReport: make(map[string]types.Evidence), } } @@ -42,7 +42,7 @@ func (p *Mock) String() string { } var vals strings.Builder - for _, v := range p.vals { + for _, v := range p.votes { fmt.Fprintf(&vals, " %X", v.Hash()) } @@ -53,25 +53,25 @@ func (p *Mock) LightBlock(_ context.Context, height int64) (*types.LightBlock, e var lb *types.LightBlock if height == 0 && len(p.headers) > 0 { sh := p.headers[int64(len(p.headers))] - vals := p.vals[int64(len(p.vals))] + vals := p.votes[int64(len(p.votes))] lb = &types.LightBlock{ SignedHeader: sh, - ValidatorSet: vals, + VoterSet: vals, } } if _, ok := p.headers[height]; ok { sh := p.headers[height] - vals := p.vals[height] + vals := p.votes[height] lb = &types.LightBlock{ SignedHeader: sh, - ValidatorSet: vals, + VoterSet: vals, } } if lb == nil { return nil, provider.ErrLightBlockNotFound } - if lb.SignedHeader == nil || lb.ValidatorSet == nil { + if lb.SignedHeader == nil || lb.VoterSet == nil { return nil, provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")} } if err := lb.ValidateBasic(lb.ChainID); err != nil { diff --git a/light/proxy/routes.go b/light/proxy/routes.go index 0ed7f9433..6940fa26e 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -142,11 +142,11 @@ func makeTxSearchFunc(c *lrpc.Client) rpcTxSearchFunc { } type rpcValidatorsFunc func(ctx *rpctypes.Context, height *int64, - page, perPage *int) (*ctypes.ResultValidators, error) + page, perPage *int) (*ctypes.ResultVoters, error) func makeValidatorsFunc(c *lrpc.Client) rpcValidatorsFunc { - return func(ctx *rpctypes.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) { - return c.Validators(ctx.Context(), height, page, perPage) + return func(ctx *rpctypes.Context, height *int64, page, perPage *int) (*ctypes.ResultVoters, error) { + return c.Voters(ctx.Context(), height, page, perPage) } } diff --git a/light/rpc/client.go b/light/rpc/client.go index c44479f15..bd47d37c5 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -453,8 +453,8 @@ func (c *Client) TxSearch(ctx context.Context, query string, prove bool, page, p return c.next.TxSearch(ctx, query, prove, page, perPage, orderBy) } -// Validators fetches and verifies validators. -func (c *Client) Validators(ctx context.Context, height *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators, +// Voters fetches and verifies validators. +func (c *Client) Voters(ctx context.Context, height *int64, pagePtr, perPagePtr *int) (*ctypes.ResultVoters, error) { // Update the light client if we're behind and retrieve the light block at the requested height // or at the latest height if no height is provided. @@ -474,11 +474,19 @@ func (c *Client) Validators(ctx context.Context, height *int64, pagePtr, perPage v := l.ValidatorSet.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)] - return &ctypes.ResultValidators{ - BlockHeight: l.Height, - Validators: v, - Count: len(v), - Total: totalCount}, nil + voterIndices := make([]int, 0) + for i := range v { + if j, _ := l.VoterSet.GetByAddress(v[i].Address); j >= 0 { + voterIndices = append(voterIndices, j) + } + } + + return &ctypes.ResultVoters{ + BlockHeight: l.Height, + Validators: v, + VoterIndices: voterIndices, + Count: len(v), + Total: totalCount}, nil } func (c *Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { diff --git a/light/store/db/db_test.go b/light/store/db/db_test.go index ef9710694..609564c7b 100644 --- a/light/store/db/db_test.go +++ b/light/store/db/db_test.go @@ -171,27 +171,27 @@ func Test_Concurrency(t *testing.T) { } func randLightBlock(height int64) *types.LightBlock { - vals, _ := types.RandValidatorSet(2, 1) + _, voters, _ := types.RandVoterSet(2, 1) return &types.LightBlock{ SignedHeader: &types.SignedHeader{ Header: &types.Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: 0}, - ChainID: tmrand.Str(12), - Height: height, - Time: time.Now(), - LastBlockID: types.BlockID{}, - LastCommitHash: crypto.CRandBytes(tmhash.Size), - DataHash: crypto.CRandBytes(tmhash.Size), - ValidatorsHash: crypto.CRandBytes(tmhash.Size), - NextValidatorsHash: crypto.CRandBytes(tmhash.Size), - ConsensusHash: crypto.CRandBytes(tmhash.Size), - AppHash: crypto.CRandBytes(tmhash.Size), - LastResultsHash: crypto.CRandBytes(tmhash.Size), - EvidenceHash: crypto.CRandBytes(tmhash.Size), - ProposerAddress: crypto.CRandBytes(crypto.AddressSize), + Version: tmversion.Consensus{Block: version.BlockProtocol, App: 0}, + ChainID: tmrand.Str(12), + Height: height, + Time: time.Now(), + LastBlockID: types.BlockID{}, + LastCommitHash: crypto.CRandBytes(tmhash.Size), + DataHash: crypto.CRandBytes(tmhash.Size), + VotersHash: crypto.CRandBytes(tmhash.Size), + NextVotersHash: crypto.CRandBytes(tmhash.Size), + ConsensusHash: crypto.CRandBytes(tmhash.Size), + AppHash: crypto.CRandBytes(tmhash.Size), + LastResultsHash: crypto.CRandBytes(tmhash.Size), + EvidenceHash: crypto.CRandBytes(tmhash.Size), + ProposerAddress: crypto.CRandBytes(crypto.AddressSize), }, Commit: &types.Commit{}, }, - ValidatorSet: vals, + VoterSet: voters, } } diff --git a/light/verifier.go b/light/verifier.go index 0b0a4926b..547397be3 100644 --- a/light/verifier.go +++ b/light/verifier.go @@ -31,9 +31,9 @@ var ( // future. func VerifyNonAdjacent( trustedHeader *types.SignedHeader, // height=X - trustedVals *types.ValidatorSet, // height=X or height=X+1 + trustedVals *types.VoterSet, // height=X or height=X+1 untrustedHeader *types.SignedHeader, // height=Y - untrustedVals *types.ValidatorSet, // height=Y + untrustedVals *types.VoterSet, // height=Y trustingPeriod time.Duration, now time.Time, maxClockDrift time.Duration, @@ -93,7 +93,7 @@ func VerifyNonAdjacent( func VerifyAdjacent( trustedHeader *types.SignedHeader, // height=X untrustedHeader *types.SignedHeader, // height=X+1 - untrustedVals *types.ValidatorSet, // height=X+1 + untrustedVals *types.VoterSet, // height=X+1 trustingPeriod time.Duration, now time.Time, maxClockDrift time.Duration) error { @@ -114,10 +114,10 @@ func VerifyAdjacent( } // Check the validator hashes are the same - if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) { + if !bytes.Equal(untrustedHeader.VotersHash, trustedHeader.NextVotersHash) { err := fmt.Errorf("expected old header next validators (%X) to match those from new header (%X)", - trustedHeader.NextValidatorsHash, - untrustedHeader.ValidatorsHash, + trustedHeader.NextVotersHash, + untrustedHeader.VotersHash, ) return err } @@ -134,9 +134,9 @@ func VerifyAdjacent( // Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. func Verify( trustedHeader *types.SignedHeader, // height=X - trustedVals *types.ValidatorSet, // height=X or height=X+1 + trustedVals *types.VoterSet, // height=X or height=X+1 untrustedHeader *types.SignedHeader, // height=Y - untrustedVals *types.ValidatorSet, // height=Y + untrustedVals *types.VoterSet, // height=Y trustingPeriod time.Duration, now time.Time, maxClockDrift time.Duration, @@ -152,7 +152,7 @@ func Verify( func verifyNewHeaderAndVals( untrustedHeader *types.SignedHeader, - untrustedVals *types.ValidatorSet, + untrustedVals *types.VoterSet, trustedHeader *types.SignedHeader, now time.Time, maxClockDrift time.Duration) error { @@ -180,9 +180,9 @@ func verifyNewHeaderAndVals( maxClockDrift) } - if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) { + if !bytes.Equal(untrustedHeader.VotersHash, untrustedVals.Hash()) { return fmt.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d", - untrustedHeader.ValidatorsHash, + untrustedHeader.VotersHash, untrustedVals.Hash(), untrustedHeader.Height, ) diff --git a/light/verifier_test.go b/light/verifier_test.go index 9e10810b2..632c4225d 100644 --- a/light/verifier_test.go +++ b/light/verifier_test.go @@ -26,7 +26,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { var ( keys = genPrivKeys(4) // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) + vals = types.ToVoterAll(keys.ToValidators(20, 10)) bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") header = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) @@ -34,7 +34,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { testCases := []struct { newHeader *types.SignedHeader - newVals *types.ValidatorSet + newVals *types.VoterSet trustingPeriod time.Duration now time.Time expErr error @@ -122,9 +122,10 @@ func TestVerifyAdjacentHeaders(t *testing.T) { }, // vals does not match with what we have -> error 8: { - keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, keys.ToValidators(10, 1), vals, - hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), - keys.ToValidators(10, 1), + keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, + types.ToVoterAll(keys.ToValidators(10, 1)), vals, hash("app_hash"), hash("cons_hash"), + hash("results_hash"), 0, len(keys)), + types.ToVoterAll(keys.ToValidators(10, 1)), 3 * time.Hour, bTime.Add(2 * time.Hour), nil, @@ -134,7 +135,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { 9: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), - keys.ToValidators(10, 1), + types.ToVoterAll(keys.ToValidators(10, 1)), 3 * time.Hour, bTime.Add(2 * time.Hour), nil, @@ -144,7 +145,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { 10: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)), - keys.ToValidators(10, 1), + types.ToVoterAll(keys.ToValidators(10, 1)), 1 * time.Hour, bTime.Add(1 * time.Hour), nil, @@ -178,27 +179,27 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { var ( keys = genPrivKeys(4) // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) + vals = types.ToVoterAll(keys.ToValidators(20, 10)) bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") header = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) // 30, 40, 50 twoThirds = keys[1:] - twoThirdsVals = twoThirds.ToValidators(30, 10) + twoThirdsVals = types.ToVoterAll(twoThirds.ToValidators(30, 10)) // 50 oneThird = keys[len(keys)-1:] - oneThirdVals = oneThird.ToValidators(50, 10) + oneThirdVals = types.ToVoterAll(oneThird.ToValidators(50, 10)) // 20 lessThanOneThird = keys[0:1] - lessThanOneThirdVals = lessThanOneThird.ToValidators(20, 10) + lessThanOneThirdVals = types.ToVoterAll(lessThanOneThird.ToValidators(20, 10)) ) testCases := []struct { newHeader *types.SignedHeader - newVals *types.ValidatorSet + newVals *types.VoterSet trustingPeriod time.Duration now time.Time expErr error @@ -294,7 +295,7 @@ func TestVerifyReturnsErrorIfTrustLevelIsInvalid(t *testing.T) { var ( keys = genPrivKeys(4) // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) + vals = types.ToVoterAll(keys.ToValidators(20, 10)) bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") header = keys.GenSignedHeader(chainID, lastHeight, bTime, nil, vals, vals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)) diff --git a/node/node_test.go b/node/node_test.go index 8e37d1774..1aadef9ca 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -445,7 +445,7 @@ func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) { for i := 1; i < int(height); i++ { s.LastBlockHeight++ - s.LastValidators = s.Validators.Copy() + s.LastVoters = s.Voters.Copy() if err := stateStore.Save(s); err != nil { panic(err) } diff --git a/proto/tendermint/state/types.pb.go b/proto/tendermint/state/types.pb.go index f51b73e30..c31b59b60 100644 --- a/proto/tendermint/state/types.pb.go +++ b/proto/tendermint/state/types.pb.go @@ -97,6 +97,8 @@ func (m *ABCIResponses) GetBeginBlock() *types.ResponseBeginBlock { type ValidatorsInfo struct { ValidatorSet *types1.ValidatorSet `protobuf:"bytes,1,opt,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"` LastHeightChanged int64 `protobuf:"varint,2,opt,name=last_height_changed,json=lastHeightChanged,proto3" json:"last_height_changed,omitempty"` + // the VRF Proof value generated by the last Proposer + ProofHash []byte `protobuf:"bytes,1000,opt,name=proof_hash,json=proofHash,proto3" json:"proof_hash,omitempty"` } func (m *ValidatorsInfo) Reset() { *m = ValidatorsInfo{} } @@ -146,6 +148,13 @@ func (m *ValidatorsInfo) GetLastHeightChanged() int64 { return 0 } +func (m *ValidatorsInfo) GetProofHash() []byte { + if m != nil { + return m.ProofHash + } + return nil +} + // ConsensusParamsInfo represents the latest consensus params, or the last height it changed type ConsensusParamsInfo struct { ConsensusParams types1.ConsensusParams `protobuf:"bytes,1,opt,name=consensus_params,json=consensusParams,proto3" json:"consensus_params"` @@ -266,10 +275,10 @@ type State struct { // Note that if s.LastBlockHeight causes a valset change, // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 // Extra +1 due to nextValSet delay. - NextValidators *types1.ValidatorSet `protobuf:"bytes,6,opt,name=next_validators,json=nextValidators,proto3" json:"next_validators,omitempty"` - Validators *types1.ValidatorSet `protobuf:"bytes,7,opt,name=validators,proto3" json:"validators,omitempty"` - LastValidators *types1.ValidatorSet `protobuf:"bytes,8,opt,name=last_validators,json=lastValidators,proto3" json:"last_validators,omitempty"` - LastHeightValidatorsChanged int64 `protobuf:"varint,9,opt,name=last_height_validators_changed,json=lastHeightValidatorsChanged,proto3" json:"last_height_validators_changed,omitempty"` + NextValidators *types1.ValidatorSet `protobuf:"bytes,6,opt,name=next_validators,json=nextValidators,proto3" json:"next_validators,omitempty"` + Validators *types1.ValidatorSet `protobuf:"bytes,7,opt,name=validators,proto3" json:"validators,omitempty"` + // tendermint.types.ValidatorSet last_validators = 8; 🏺 replace with last_voters + LastHeightValidatorsChanged int64 `protobuf:"varint,9,opt,name=last_height_validators_changed,json=lastHeightValidatorsChanged,proto3" json:"last_height_validators_changed,omitempty"` // Consensus parameters used for validating blocks. // Changes returned by EndBlock and updated after Commit. ConsensusParams types1.ConsensusParams `protobuf:"bytes,10,opt,name=consensus_params,json=consensusParams,proto3" json:"consensus_params"` @@ -280,6 +289,10 @@ type State struct { AppHash []byte `protobuf:"bytes,13,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` // the VRF Proof value generated by the last Proposer LastProofHash []byte `protobuf:"bytes,1000,opt,name=last_proof_hash,json=lastProofHash,proto3" json:"last_proof_hash,omitempty"` + // before and after the round voter set + Voters *types1.VoterSet `protobuf:"bytes,1001,opt,name=voters,proto3" json:"voters,omitempty"` + NextVoters *types1.VoterSet `protobuf:"bytes,1002,opt,name=next_voters,json=nextVoters,proto3" json:"next_voters,omitempty"` + LastVoters *types1.VoterSet `protobuf:"bytes,1003,opt,name=last_voters,json=lastVoters,proto3" json:"last_voters,omitempty"` } func (m *State) Reset() { *m = State{} } @@ -371,13 +384,6 @@ func (m *State) GetValidators() *types1.ValidatorSet { return nil } -func (m *State) GetLastValidators() *types1.ValidatorSet { - if m != nil { - return m.LastValidators - } - return nil -} - func (m *State) GetLastHeightValidatorsChanged() int64 { if m != nil { return m.LastHeightValidatorsChanged @@ -420,6 +426,27 @@ func (m *State) GetLastProofHash() []byte { return nil } +func (m *State) GetVoters() *types1.VoterSet { + if m != nil { + return m.Voters + } + return nil +} + +func (m *State) GetNextVoters() *types1.VoterSet { + if m != nil { + return m.NextVoters + } + return nil +} + +func (m *State) GetLastVoters() *types1.VoterSet { + if m != nil { + return m.LastVoters + } + return nil +} + func init() { proto.RegisterType((*ABCIResponses)(nil), "tendermint.state.ABCIResponses") proto.RegisterType((*ValidatorsInfo)(nil), "tendermint.state.ValidatorsInfo") @@ -431,57 +458,60 @@ func init() { func init() { proto.RegisterFile("tendermint/state/types.proto", fileDescriptor_ccfacf933f22bf93) } var fileDescriptor_ccfacf933f22bf93 = []byte{ - // 785 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xcd, 0x6e, 0xf3, 0x44, - 0x14, 0x8d, 0xc9, 0xd7, 0x3a, 0x19, 0x37, 0x49, 0x99, 0xb2, 0x70, 0x53, 0xea, 0xa4, 0xe1, 0xaf, - 0x62, 0xe1, 0x48, 0x65, 0x81, 0xd8, 0x54, 0xaa, 0x13, 0x44, 0x23, 0x55, 0xa8, 0xb8, 0x55, 0x17, - 0x6c, 0xac, 0x49, 0x3c, 0xb1, 0x2d, 0x12, 0xdb, 0xf2, 0x4c, 0x42, 0x79, 0x00, 0x36, 0xac, 0xba, - 0xe5, 0x8d, 0xba, 0xec, 0x12, 0xb1, 0x28, 0x28, 0xdd, 0xf0, 0x18, 0x68, 0x7e, 0x6c, 0x4f, 0x12, - 0x2a, 0x15, 0x7d, 0xbb, 0x99, 0x7b, 0xcf, 0x3d, 0x73, 0xe6, 0xce, 0xb9, 0x1a, 0xf0, 0x31, 0xc5, - 0xb1, 0x8f, 0xb3, 0x79, 0x14, 0xd3, 0x3e, 0xa1, 0x88, 0xe2, 0x3e, 0xfd, 0x25, 0xc5, 0xc4, 0x4e, - 0xb3, 0x84, 0x26, 0x70, 0xbf, 0xcc, 0xda, 0x3c, 0xdb, 0xfe, 0x28, 0x48, 0x82, 0x84, 0x27, 0xfb, - 0x6c, 0x25, 0x70, 0xed, 0x23, 0x85, 0x05, 0x8d, 0x27, 0x91, 0x4a, 0xd2, 0x56, 0x8f, 0xe0, 0xf1, - 0xb5, 0x6c, 0x77, 0x2b, 0xbb, 0x44, 0xb3, 0xc8, 0x47, 0x34, 0xc9, 0x24, 0xe2, 0x78, 0x0b, 0x91, - 0xa2, 0x0c, 0xcd, 0x73, 0x02, 0x4b, 0x49, 0x2f, 0x71, 0x46, 0xa2, 0x24, 0x5e, 0x3b, 0xa0, 0x13, - 0x24, 0x49, 0x30, 0xc3, 0x7d, 0xbe, 0x1b, 0x2f, 0xa6, 0x7d, 0x1a, 0xcd, 0x31, 0xa1, 0x68, 0x9e, - 0x0a, 0x40, 0xef, 0x4f, 0x0d, 0x34, 0x2e, 0x9c, 0xc1, 0xc8, 0xc5, 0x24, 0x4d, 0x62, 0x82, 0x09, - 0x1c, 0x00, 0xc3, 0xc7, 0xb3, 0x68, 0x89, 0x33, 0x8f, 0xde, 0x13, 0x53, 0xeb, 0x56, 0x4f, 0x8d, - 0xb3, 0x9e, 0xad, 0x34, 0x83, 0x5d, 0xd2, 0xce, 0x0b, 0x86, 0x02, 0x7b, 0x7b, 0xef, 0x02, 0x3f, - 0x5f, 0x12, 0x78, 0x0e, 0xea, 0x38, 0xf6, 0xbd, 0xf1, 0x2c, 0x99, 0xfc, 0x64, 0x7e, 0xd0, 0xd5, - 0x4e, 0x8d, 0xb3, 0x93, 0x57, 0x29, 0xbe, 0x8d, 0x7d, 0x87, 0x01, 0xdd, 0x1a, 0x96, 0x2b, 0x38, - 0x04, 0xc6, 0x18, 0x07, 0x51, 0x2c, 0x19, 0xaa, 0x9c, 0xe1, 0x93, 0x57, 0x19, 0x1c, 0x86, 0x15, - 0x1c, 0x60, 0x5c, 0xac, 0x7b, 0xbf, 0x6a, 0xa0, 0x79, 0x97, 0x37, 0x94, 0x8c, 0xe2, 0x69, 0x02, - 0x07, 0xa0, 0x51, 0xb4, 0xd8, 0x23, 0x98, 0x9a, 0x1a, 0xa7, 0xb6, 0x54, 0x6a, 0xd1, 0xc0, 0xa2, - 0xf0, 0x06, 0x53, 0x77, 0x6f, 0xa9, 0xec, 0xa0, 0x0d, 0x0e, 0x66, 0x88, 0x50, 0x2f, 0xc4, 0x51, - 0x10, 0x52, 0x6f, 0x12, 0xa2, 0x38, 0xc0, 0x3e, 0xbf, 0x67, 0xd5, 0xfd, 0x90, 0xa5, 0x2e, 0x79, - 0x66, 0x20, 0x12, 0xbd, 0xdf, 0x35, 0x70, 0x30, 0x60, 0x3a, 0x63, 0xb2, 0x20, 0xd7, 0xfc, 0xfd, - 0xb8, 0x18, 0x17, 0xec, 0x4f, 0xf2, 0xb0, 0x27, 0xde, 0x55, 0xea, 0x39, 0xd9, 0xd6, 0xb3, 0x41, - 0xe0, 0xbc, 0x7b, 0x7c, 0xee, 0x54, 0xdc, 0xd6, 0x64, 0x3d, 0xfc, 0xbf, 0xb5, 0x85, 0x40, 0xbf, - 0x13, 0xc6, 0x81, 0x17, 0xa0, 0x5e, 0xb0, 0x49, 0x1d, 0xc7, 0xaa, 0x0e, 0x69, 0xb0, 0x52, 0x89, - 0xd4, 0x50, 0x56, 0xc1, 0x36, 0xa8, 0x91, 0x64, 0x4a, 0x7f, 0x46, 0x19, 0xe6, 0x47, 0xd6, 0xdd, - 0x62, 0xdf, 0xfb, 0x4d, 0x07, 0x3b, 0x37, 0x6c, 0x8e, 0xe0, 0x37, 0x40, 0x97, 0x5c, 0xf2, 0x98, - 0x43, 0x7b, 0x73, 0xd6, 0x6c, 0x29, 0x4a, 0x1e, 0x91, 0xe3, 0xe1, 0xe7, 0xa0, 0x36, 0x09, 0x51, - 0x14, 0x7b, 0x91, 0xb8, 0x53, 0xdd, 0x31, 0x56, 0xcf, 0x1d, 0x7d, 0xc0, 0x62, 0xa3, 0xa1, 0xab, - 0xf3, 0xe4, 0xc8, 0x87, 0x9f, 0x81, 0x66, 0x14, 0x47, 0x34, 0x42, 0x33, 0xd9, 0x09, 0xb3, 0xc9, - 0x3b, 0xd0, 0x90, 0x51, 0xd1, 0x04, 0xf8, 0x25, 0xe0, 0x2d, 0x11, 0x36, 0xcb, 0x91, 0x55, 0x8e, - 0x6c, 0xb1, 0x04, 0xf7, 0x91, 0xc4, 0xba, 0xa0, 0xa1, 0x60, 0x23, 0xdf, 0x7c, 0xb7, 0xad, 0x5d, - 0x3c, 0x15, 0xaf, 0x1a, 0x0d, 0x9d, 0x03, 0xa6, 0x7d, 0xf5, 0xdc, 0x31, 0xae, 0x72, 0xaa, 0xd1, - 0xd0, 0x35, 0x0a, 0xde, 0x91, 0x0f, 0xaf, 0x40, 0x4b, 0xe1, 0x64, 0xc3, 0x69, 0xee, 0x70, 0xd6, - 0xb6, 0x2d, 0x26, 0xd7, 0xce, 0x27, 0xd7, 0xbe, 0xcd, 0x27, 0xd7, 0xa9, 0x31, 0xda, 0x87, 0xbf, - 0x3a, 0x9a, 0xdb, 0x28, 0xb8, 0x58, 0x16, 0x7e, 0x07, 0x5a, 0x31, 0xbe, 0xa7, 0x5e, 0x61, 0x56, - 0x62, 0xee, 0xbe, 0xc9, 0xde, 0x4d, 0x56, 0x56, 0x4e, 0x0a, 0x3c, 0x07, 0x40, 0xe1, 0xd0, 0xdf, - 0xc4, 0xa1, 0x54, 0x30, 0x21, 0xfc, 0x5a, 0x0a, 0x49, 0xed, 0x6d, 0x42, 0x58, 0x99, 0x22, 0x64, - 0x00, 0x2c, 0xd5, 0xcd, 0x25, 0x5f, 0x61, 0xec, 0x3a, 0x7f, 0xac, 0xa3, 0xd2, 0xd8, 0x65, 0xb5, - 0xb4, 0xf8, 0x7f, 0x8e, 0x19, 0x78, 0xcf, 0x31, 0xfb, 0x1e, 0x7c, 0xba, 0x36, 0x66, 0x1b, 0xfc, - 0x85, 0x3c, 0x83, 0xcb, 0xeb, 0x2a, 0x73, 0xb7, 0x4e, 0x94, 0x6b, 0xcc, 0x8d, 0x98, 0x61, 0xb2, - 0x98, 0x51, 0xe2, 0x85, 0x88, 0x84, 0xe6, 0x5e, 0x57, 0x3b, 0xdd, 0x13, 0x46, 0x74, 0x45, 0xfc, - 0x12, 0x91, 0x10, 0x1e, 0x82, 0x1a, 0x4a, 0x53, 0x01, 0x69, 0x70, 0x88, 0x8e, 0xd2, 0x94, 0xa7, - 0xbe, 0x90, 0x8d, 0x4f, 0xb3, 0x24, 0x99, 0x0a, 0xc4, 0x3f, 0x3a, 0x87, 0x70, 0xab, 0x5c, 0xb3, - 0x30, 0x03, 0x3a, 0x3f, 0x3c, 0xae, 0x2c, 0xed, 0x69, 0x65, 0x69, 0x7f, 0xaf, 0x2c, 0xed, 0xe1, - 0xc5, 0xaa, 0x3c, 0xbd, 0x58, 0x95, 0x3f, 0x5e, 0xac, 0xca, 0x8f, 0x5f, 0x07, 0x11, 0x0d, 0x17, - 0x63, 0x7b, 0x92, 0xcc, 0xfb, 0xea, 0xe7, 0x53, 0x2e, 0xc5, 0x0f, 0xb8, 0xf9, 0x77, 0x8e, 0x77, - 0x79, 0xfc, 0xab, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x2c, 0x94, 0x13, 0x56, 0x07, 0x00, - 0x00, + // 837 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xcd, 0xae, 0xdb, 0x44, + 0x14, 0x8e, 0x49, 0x7b, 0x9d, 0x1c, 0xdf, 0x24, 0x65, 0x2e, 0x0b, 0x37, 0xa5, 0x4e, 0x1a, 0xfe, + 0xae, 0x58, 0x38, 0x52, 0xbb, 0x40, 0x48, 0xa8, 0x52, 0x9d, 0x20, 0x1a, 0xa9, 0x42, 0xc5, 0xad, + 0xba, 0x60, 0x63, 0x4d, 0xe2, 0x89, 0x6d, 0x91, 0x78, 0x2c, 0xcf, 0x24, 0x5c, 0xde, 0xa2, 0x5b, + 0x1e, 0x80, 0x77, 0xe9, 0xb2, 0x4b, 0xc4, 0xe2, 0x82, 0x72, 0x37, 0xfc, 0xec, 0x78, 0x02, 0x34, + 0x3f, 0xfe, 0x49, 0x42, 0xc5, 0x45, 0xdd, 0x8d, 0xcf, 0xf9, 0xce, 0x37, 0xdf, 0x9c, 0xf9, 0xce, + 0x18, 0xde, 0xe7, 0x24, 0x0d, 0x49, 0xbe, 0x4e, 0x52, 0x3e, 0x66, 0x1c, 0x73, 0x32, 0xe6, 0x3f, + 0x64, 0x84, 0xb9, 0x59, 0x4e, 0x39, 0x45, 0xb7, 0xaa, 0xac, 0x2b, 0xb3, 0xfd, 0xf7, 0x22, 0x1a, + 0x51, 0x99, 0x1c, 0x8b, 0x95, 0xc2, 0xf5, 0xef, 0xd4, 0x58, 0xf0, 0x7c, 0x91, 0xd4, 0x49, 0xfa, + 0xf5, 0x2d, 0x64, 0x7c, 0x2f, 0x3b, 0x3c, 0xca, 0x6e, 0xf1, 0x2a, 0x09, 0x31, 0xa7, 0xb9, 0x46, + 0xdc, 0x3d, 0x42, 0x64, 0x38, 0xc7, 0xeb, 0x82, 0xc0, 0xa9, 0xa5, 0xb7, 0x24, 0x67, 0x09, 0x4d, + 0xf7, 0x36, 0x18, 0x44, 0x94, 0x46, 0x2b, 0x32, 0x96, 0x5f, 0xf3, 0xcd, 0x72, 0xcc, 0x93, 0x35, + 0x61, 0x1c, 0xaf, 0x33, 0x05, 0x18, 0xfd, 0x62, 0x40, 0xe7, 0x91, 0x37, 0x99, 0xf9, 0x84, 0x65, + 0x34, 0x65, 0x84, 0xa1, 0x09, 0x58, 0x21, 0x59, 0x25, 0x5b, 0x92, 0x07, 0xfc, 0x82, 0xd9, 0xc6, + 0xb0, 0x79, 0x6e, 0xdd, 0x1f, 0xb9, 0xb5, 0x66, 0x88, 0x43, 0xba, 0x45, 0xc1, 0x54, 0x61, 0x9f, + 0x5f, 0xf8, 0x10, 0x16, 0x4b, 0x86, 0x1e, 0x42, 0x9b, 0xa4, 0x61, 0x30, 0x5f, 0xd1, 0xc5, 0x77, + 0xf6, 0x3b, 0x43, 0xe3, 0xdc, 0xba, 0x7f, 0xef, 0x8d, 0x14, 0x5f, 0xa6, 0xa1, 0x27, 0x80, 0x7e, + 0x8b, 0xe8, 0x15, 0x9a, 0x82, 0x35, 0x27, 0x51, 0x92, 0x6a, 0x86, 0xa6, 0x64, 0xf8, 0xe0, 0x8d, + 0x0c, 0x9e, 0xc0, 0x2a, 0x0e, 0x98, 0x97, 0xeb, 0xd1, 0x4f, 0x06, 0x74, 0x5f, 0x14, 0x0d, 0x65, + 0xb3, 0x74, 0x49, 0xd1, 0x04, 0x3a, 0x65, 0x8b, 0x03, 0x46, 0xb8, 0x6d, 0x48, 0x6a, 0xa7, 0x4e, + 0xad, 0x1a, 0x58, 0x16, 0x3e, 0x23, 0xdc, 0x3f, 0xdd, 0xd6, 0xbe, 0x90, 0x0b, 0x67, 0x2b, 0xcc, + 0x78, 0x10, 0x93, 0x24, 0x8a, 0x79, 0xb0, 0x88, 0x71, 0x1a, 0x91, 0x50, 0x9e, 0xb3, 0xe9, 0xbf, + 0x2b, 0x52, 0x8f, 0x65, 0x66, 0xa2, 0x12, 0xc8, 0x01, 0xc8, 0x72, 0x4a, 0x97, 0x41, 0x8c, 0x59, + 0x6c, 0xff, 0x6e, 0x0e, 0x8d, 0xf3, 0x53, 0xbf, 0x2d, 0x43, 0x8f, 0x31, 0x8b, 0x47, 0x3f, 0x1a, + 0x70, 0x36, 0x11, 0xe7, 0x48, 0xd9, 0x86, 0x3d, 0x95, 0xf7, 0x2b, 0xc5, 0xfa, 0x70, 0x6b, 0x51, + 0x84, 0x03, 0x75, 0xef, 0x5a, 0xef, 0xbd, 0x63, 0xbd, 0x07, 0x04, 0xde, 0x8d, 0x57, 0x97, 0x83, + 0x86, 0xdf, 0x5b, 0xec, 0x87, 0xff, 0xaf, 0xf6, 0x51, 0x0c, 0xe6, 0x0b, 0x65, 0x2c, 0xf4, 0x08, + 0xda, 0x25, 0x9b, 0xd6, 0x71, 0xb7, 0xae, 0x43, 0x1b, 0xb0, 0x52, 0xa2, 0x35, 0x54, 0x55, 0xa8, + 0x0f, 0x2d, 0x46, 0x97, 0xfc, 0x7b, 0x9c, 0x13, 0xb9, 0x65, 0xdb, 0x2f, 0xbf, 0x47, 0x7f, 0x9b, + 0x70, 0xf3, 0x99, 0x98, 0x33, 0xf4, 0x39, 0x98, 0x9a, 0x4b, 0x6f, 0x73, 0xdb, 0x3d, 0x9c, 0x45, + 0x57, 0x8b, 0xd2, 0x5b, 0x14, 0x78, 0xf4, 0x31, 0xb4, 0x16, 0x31, 0x4e, 0xd2, 0x20, 0x51, 0x67, + 0x6a, 0x7b, 0xd6, 0xee, 0x72, 0x60, 0x4e, 0x44, 0x6c, 0x36, 0xf5, 0x4d, 0x99, 0x9c, 0x85, 0xe8, + 0x23, 0xe8, 0x26, 0x69, 0xc2, 0x13, 0xbc, 0xd2, 0x9d, 0xb0, 0xbb, 0xb2, 0x03, 0x1d, 0x1d, 0x55, + 0x4d, 0x40, 0x9f, 0x82, 0x6c, 0x89, 0xb2, 0x61, 0x81, 0x6c, 0x4a, 0x64, 0x4f, 0x24, 0xa4, 0xcf, + 0x34, 0xd6, 0x87, 0x4e, 0x0d, 0x9b, 0x84, 0xf6, 0x8d, 0x63, 0xed, 0xea, 0xaa, 0x64, 0xd5, 0x6c, + 0xea, 0x9d, 0x09, 0xed, 0xbb, 0xcb, 0x81, 0xf5, 0xa4, 0xa0, 0x9a, 0x4d, 0x7d, 0xab, 0xe4, 0x9d, + 0x85, 0xe8, 0x09, 0xf4, 0x6a, 0x9c, 0x62, 0x78, 0xed, 0x9b, 0x92, 0xb5, 0xef, 0xaa, 0xc9, 0x76, + 0x8b, 0xc9, 0x76, 0x9f, 0x17, 0x93, 0xed, 0xb5, 0x04, 0xed, 0xcb, 0x5f, 0x07, 0x86, 0xdf, 0x29, + 0xb9, 0x44, 0x16, 0x7d, 0x05, 0xbd, 0x94, 0x5c, 0xf0, 0xa0, 0x34, 0x33, 0xb3, 0x4f, 0xae, 0x65, + 0xff, 0xae, 0x28, 0xab, 0x26, 0x09, 0x3d, 0x04, 0xa8, 0x71, 0x98, 0xd7, 0xe2, 0xa8, 0x55, 0xa0, + 0x09, 0x38, 0x75, 0x13, 0x56, 0x99, 0xd2, 0x8f, 0x6d, 0xd9, 0xe3, 0x3b, 0x95, 0x1f, 0xab, 0xdd, + 0x8b, 0xa9, 0xfa, 0xb7, 0xe9, 0x80, 0xb7, 0x9c, 0x8e, 0xaf, 0xe1, 0xc3, 0xbd, 0xe9, 0x38, 0xe0, + 0x2f, 0xe5, 0x59, 0x52, 0xde, 0xb0, 0x36, 0x2e, 0xfb, 0x44, 0x85, 0xc6, 0xc2, 0x3f, 0x39, 0x61, + 0x9b, 0x15, 0x67, 0xea, 0x01, 0x38, 0x95, 0xf3, 0x2f, 0x2f, 0xd6, 0x57, 0x71, 0xf1, 0x0a, 0xa0, + 0xdb, 0xd0, 0xc2, 0x59, 0xa6, 0x20, 0x1d, 0x09, 0x31, 0x71, 0x96, 0xc9, 0xd4, 0x27, 0xda, 0x06, + 0xc7, 0xaf, 0x88, 0xbc, 0xe1, 0xa7, 0xc5, 0x4b, 0x82, 0x1e, 0xc0, 0xc9, 0x96, 0x72, 0x92, 0x33, + 0xfb, 0x0f, 0x53, 0xfb, 0xe4, 0xf8, 0x56, 0x04, 0x40, 0xdc, 0x88, 0x86, 0xa2, 0x2f, 0xc0, 0x52, + 0xb6, 0x50, 0x95, 0x7f, 0xfe, 0x77, 0x25, 0x48, 0x3f, 0x94, 0xd5, 0x52, 0x9b, 0xae, 0xfe, 0xeb, + 0x1a, 0xd5, 0x02, 0xaf, 0xaa, 0xbd, 0x6f, 0x5e, 0xed, 0x1c, 0xe3, 0xf5, 0xce, 0x31, 0x7e, 0xdb, + 0x39, 0xc6, 0xcb, 0x2b, 0xa7, 0xf1, 0xfa, 0xca, 0x69, 0xfc, 0x7c, 0xe5, 0x34, 0xbe, 0xfd, 0x2c, + 0x4a, 0x78, 0xbc, 0x99, 0xbb, 0x0b, 0xba, 0x1e, 0xd7, 0x7f, 0x82, 0xd5, 0x52, 0xfd, 0x89, 0x0f, + 0xff, 0xe1, 0xf3, 0x13, 0x19, 0x7f, 0xf0, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xe2, 0x6b, + 0x54, 0xde, 0x07, 0x00, 0x00, } func (m *ABCIResponses) Marshal() (dAtA []byte, err error) { @@ -565,6 +595,15 @@ func (m *ValidatorsInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ProofHash) > 0 { + i -= len(m.ProofHash) + copy(dAtA[i:], m.ProofHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ProofHash))) + i-- + dAtA[i] = 0x3e + i-- + dAtA[i] = 0xc2 + } if m.LastHeightChanged != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.LastHeightChanged)) i-- @@ -683,6 +722,48 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.LastVoters != nil { + { + size, err := m.LastVoters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3e + i-- + dAtA[i] = 0xda + } + if m.NextVoters != nil { + { + size, err := m.NextVoters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3e + i-- + dAtA[i] = 0xd2 + } + if m.Voters != nil { + { + size, err := m.Voters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3e + i-- + dAtA[i] = 0xca + } if len(m.LastProofHash) > 0 { i -= len(m.LastProofHash) copy(dAtA[i:], m.LastProofHash) @@ -731,18 +812,6 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x48 } - if m.LastValidators != nil { - { - size, err := m.LastValidators.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x42 - } if m.Validators != nil { { size, err := m.Validators.MarshalToSizedBuffer(dAtA[:i]) @@ -767,12 +836,12 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastBlockTime):]) - if err10 != nil { - return 0, err10 + n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastBlockTime):]) + if err12 != nil { + return 0, err12 } - i -= n10 - i = encodeVarintTypes(dAtA, i, uint64(n10)) + i -= n12 + i = encodeVarintTypes(dAtA, i, uint64(n12)) i-- dAtA[i] = 0x2a { @@ -857,6 +926,10 @@ func (m *ValidatorsInfo) Size() (n int) { if m.LastHeightChanged != 0 { n += 1 + sovTypes(uint64(m.LastHeightChanged)) } + l = len(m.ProofHash) + if l > 0 { + n += 2 + l + sovTypes(uint64(l)) + } return n } @@ -916,10 +989,6 @@ func (m *State) Size() (n int) { l = m.Validators.Size() n += 1 + l + sovTypes(uint64(l)) } - if m.LastValidators != nil { - l = m.LastValidators.Size() - n += 1 + l + sovTypes(uint64(l)) - } if m.LastHeightValidatorsChanged != 0 { n += 1 + sovTypes(uint64(m.LastHeightValidatorsChanged)) } @@ -943,6 +1012,18 @@ func (m *State) Size() (n int) { if l > 0 { n += 2 + l + sovTypes(uint64(l)) } + if m.Voters != nil { + l = m.Voters.Size() + n += 2 + l + sovTypes(uint64(l)) + } + if m.NextVoters != nil { + l = m.NextVoters.Size() + n += 2 + l + sovTypes(uint64(l)) + } + if m.LastVoters != nil { + l = m.LastVoters.Size() + n += 2 + l + sovTypes(uint64(l)) + } return n } @@ -1192,6 +1273,40 @@ func (m *ValidatorsInfo) Unmarshal(dAtA []byte) error { break } } + case 1000: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofHash = append(m.ProofHash[:0], dAtA[iNdEx:postIndex]...) + if m.ProofHash == nil { + m.ProofHash = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -1681,42 +1796,6 @@ func (m *State) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastValidators", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.LastValidators == nil { - m.LastValidators = &types1.ValidatorSet{} - } - if err := m.LastValidators.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LastHeightValidatorsChanged", wireType) @@ -1909,6 +1988,114 @@ func (m *State) Unmarshal(dAtA []byte) error { m.LastProofHash = []byte{} } iNdEx = postIndex + case 1001: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Voters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Voters == nil { + m.Voters = &types1.VoterSet{} + } + if err := m.Voters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1002: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextVoters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NextVoters == nil { + m.NextVoters = &types1.VoterSet{} + } + if err := m.NextVoters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1003: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastVoters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LastVoters == nil { + m.LastVoters = &types1.VoterSet{} + } + if err := m.LastVoters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/state/types.proto b/proto/tendermint/state/types.proto index 2c7b91c43..c7e6eb597 100644 --- a/proto/tendermint/state/types.proto +++ b/proto/tendermint/state/types.proto @@ -24,6 +24,11 @@ message ABCIResponses { message ValidatorsInfo { tendermint.types.ValidatorSet validator_set = 1; int64 last_height_changed = 2; + + // *** Ostracon Extended Fields *** + + // the VRF Proof value generated by the last Proposer + bytes proof_hash = 1000; } // ConsensusParamsInfo represents the latest consensus params, or the last height it changed @@ -59,7 +64,7 @@ message State { // Extra +1 due to nextValSet delay. tendermint.types.ValidatorSet next_validators = 6; tendermint.types.ValidatorSet validators = 7; - tendermint.types.ValidatorSet last_validators = 8; + // tendermint.types.ValidatorSet last_validators = 8; 🏺 replace with last_voters int64 last_height_validators_changed = 9; // Consensus parameters used for validating blocks. @@ -77,4 +82,9 @@ message State { // the VRF Proof value generated by the last Proposer bytes last_proof_hash = 1000; + + // before and after the round voter set + tendermint.types.VoterSet voters = 1001; + tendermint.types.VoterSet next_voters = 1002; + tendermint.types.VoterSet last_voters = 1003; } diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index 051f6ecf4..167197186 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -274,11 +274,11 @@ type Header struct { LastCommitHash []byte `protobuf:"bytes,6,opt,name=last_commit_hash,json=lastCommitHash,proto3" json:"last_commit_hash,omitempty"` DataHash []byte `protobuf:"bytes,7,opt,name=data_hash,json=dataHash,proto3" json:"data_hash,omitempty"` // hashes from the app output from the prev block - ValidatorsHash []byte `protobuf:"bytes,8,opt,name=validators_hash,json=validatorsHash,proto3" json:"validators_hash,omitempty"` - NextValidatorsHash []byte `protobuf:"bytes,9,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"` - ConsensusHash []byte `protobuf:"bytes,10,opt,name=consensus_hash,json=consensusHash,proto3" json:"consensus_hash,omitempty"` - AppHash []byte `protobuf:"bytes,11,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` - LastResultsHash []byte `protobuf:"bytes,12,opt,name=last_results_hash,json=lastResultsHash,proto3" json:"last_results_hash,omitempty"` + VotersHash []byte `protobuf:"bytes,8,opt,name=voters_hash,json=votersHash,proto3" json:"voters_hash,omitempty"` + NextVotersHash []byte `protobuf:"bytes,9,opt,name=next_voters_hash,json=nextVotersHash,proto3" json:"next_voters_hash,omitempty"` + ConsensusHash []byte `protobuf:"bytes,10,opt,name=consensus_hash,json=consensusHash,proto3" json:"consensus_hash,omitempty"` + AppHash []byte `protobuf:"bytes,11,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + LastResultsHash []byte `protobuf:"bytes,12,opt,name=last_results_hash,json=lastResultsHash,proto3" json:"last_results_hash,omitempty"` // consensus info EvidenceHash []byte `protobuf:"bytes,13,opt,name=evidence_hash,json=evidenceHash,proto3" json:"evidence_hash,omitempty"` ProposerAddress []byte `protobuf:"bytes,14,opt,name=proposer_address,json=proposerAddress,proto3" json:"proposer_address,omitempty"` @@ -369,16 +369,16 @@ func (m *Header) GetDataHash() []byte { return nil } -func (m *Header) GetValidatorsHash() []byte { +func (m *Header) GetVotersHash() []byte { if m != nil { - return m.ValidatorsHash + return m.VotersHash } return nil } -func (m *Header) GetNextValidatorsHash() []byte { +func (m *Header) GetNextVotersHash() []byte { if m != nil { - return m.NextValidatorsHash + return m.NextVotersHash } return nil } @@ -866,7 +866,7 @@ func (m *SignedHeader) GetCommit() *Commit { type LightBlock struct { SignedHeader *SignedHeader `protobuf:"bytes,1,opt,name=signed_header,json=signedHeader,proto3" json:"signed_header,omitempty"` - ValidatorSet *ValidatorSet `protobuf:"bytes,2,opt,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty"` + VoterSet *VoterSet `protobuf:"bytes,2,opt,name=voter_set,json=voterSet,proto3" json:"voter_set,omitempty"` } func (m *LightBlock) Reset() { *m = LightBlock{} } @@ -909,9 +909,9 @@ func (m *LightBlock) GetSignedHeader() *SignedHeader { return nil } -func (m *LightBlock) GetValidatorSet() *ValidatorSet { +func (m *LightBlock) GetVoterSet() *VoterSet { if m != nil { - return m.ValidatorSet + return m.VoterSet } return nil } @@ -1066,91 +1066,91 @@ func init() { func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) } var fileDescriptor_d3a6e55e2345de56 = []byte{ - // 1330 bytes of a gzipped FileDescriptorProto + // 1329 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4f, 0x73, 0xdb, 0x44, - 0x14, 0x8f, 0x6c, 0x39, 0xb6, 0x9f, 0xed, 0xc4, 0xd1, 0xa4, 0xad, 0xeb, 0x36, 0x8e, 0xc6, 0x0c, - 0x90, 0x16, 0x46, 0x29, 0x29, 0xc3, 0x9f, 0x03, 0x07, 0xdb, 0x49, 0x5b, 0x4f, 0x13, 0xc7, 0xc8, - 0x6e, 0x19, 0xb8, 0x68, 0x64, 0x6b, 0x6b, 0x8b, 0xca, 0x92, 0x46, 0xbb, 0x0e, 0x49, 0x3f, 0x01, - 0x93, 0x53, 0x0f, 0x0c, 0xb7, 0x9c, 0xe0, 0xc0, 0x9d, 0x2f, 0xc0, 0x70, 0xea, 0xb1, 0x37, 0xb8, - 0x50, 0x98, 0xf4, 0x52, 0xbe, 0x05, 0xb3, 0x7f, 0x24, 0xcb, 0x71, 0x0c, 0x9d, 0x4c, 0x87, 0x8b, - 0x67, 0xf7, 0xbd, 0xdf, 0xdb, 0x7d, 0xfb, 0x7b, 0xbf, 0xdd, 0x27, 0xc3, 0x75, 0x82, 0x5c, 0x0b, - 0x05, 0x23, 0xdb, 0x25, 0x9b, 0xe4, 0xc8, 0x47, 0x98, 0xff, 0x6a, 0x7e, 0xe0, 0x11, 0x4f, 0x29, - 0x4e, 0xbc, 0x1a, 0xb3, 0x97, 0x57, 0x07, 0xde, 0xc0, 0x63, 0xce, 0x4d, 0x3a, 0xe2, 0xb8, 0xf2, - 0xfa, 0xc0, 0xf3, 0x06, 0x0e, 0xda, 0x64, 0xb3, 0xde, 0xf8, 0xd1, 0x26, 0xb1, 0x47, 0x08, 0x13, - 0x73, 0xe4, 0x0b, 0xc0, 0x5a, 0x6c, 0x9b, 0x7e, 0x70, 0xe4, 0x13, 0x8f, 0x62, 0xbd, 0x47, 0xc2, - 0x5d, 0x89, 0xb9, 0x0f, 0x50, 0x80, 0x6d, 0xcf, 0x8d, 0xe7, 0x51, 0x56, 0x67, 0xb2, 0x3c, 0x30, - 0x1d, 0xdb, 0x32, 0x89, 0x17, 0x70, 0x44, 0xf5, 0x53, 0x28, 0xb4, 0xcd, 0x80, 0x74, 0x10, 0xb9, - 0x87, 0x4c, 0x0b, 0x05, 0xca, 0x2a, 0xa4, 0x88, 0x47, 0x4c, 0xa7, 0x24, 0xa9, 0xd2, 0x46, 0x41, - 0xe7, 0x13, 0x45, 0x01, 0x79, 0x68, 0xe2, 0x61, 0x29, 0xa1, 0x4a, 0x1b, 0x79, 0x9d, 0x8d, 0xab, - 0x43, 0x90, 0x69, 0x28, 0x8d, 0xb0, 0x5d, 0x0b, 0x1d, 0x86, 0x11, 0x6c, 0x42, 0xad, 0xbd, 0x23, - 0x82, 0xb0, 0x08, 0xe1, 0x13, 0xe5, 0x43, 0x48, 0xb1, 0xfc, 0x4b, 0x49, 0x55, 0xda, 0xc8, 0x6d, - 0x95, 0xb4, 0x18, 0x51, 0xfc, 0x7c, 0x5a, 0x9b, 0xfa, 0xeb, 0xf2, 0xb3, 0x17, 0xeb, 0x0b, 0x3a, - 0x07, 0x57, 0x1d, 0x48, 0xd7, 0x1d, 0xaf, 0xff, 0xb8, 0xb9, 0x1d, 0x25, 0x22, 0x4d, 0x12, 0x51, - 0xf6, 0x60, 0xd9, 0x37, 0x03, 0x62, 0x60, 0x44, 0x8c, 0x21, 0x3b, 0x05, 0xdb, 0x34, 0xb7, 0xb5, - 0xae, 0x9d, 0xad, 0x83, 0x36, 0x75, 0x58, 0xb1, 0x4b, 0xc1, 0x8f, 0x1b, 0xab, 0xdf, 0xa5, 0x60, - 0x51, 0x90, 0xf1, 0x19, 0xa4, 0x05, 0xad, 0x6c, 0xc3, 0xdc, 0xd6, 0x5a, 0x7c, 0x45, 0xe1, 0xd2, - 0x1a, 0x9e, 0x8b, 0x91, 0x8b, 0xc7, 0x58, 0xac, 0x17, 0xc6, 0x28, 0xef, 0x40, 0xa6, 0x3f, 0x34, - 0x6d, 0xd7, 0xb0, 0x2d, 0x96, 0x51, 0xb6, 0x9e, 0x3b, 0x7d, 0xb1, 0x9e, 0x6e, 0x50, 0x5b, 0x73, - 0x5b, 0x4f, 0x33, 0x67, 0xd3, 0x52, 0x2e, 0xc3, 0xe2, 0x10, 0xd9, 0x83, 0x21, 0x61, 0xb4, 0x24, - 0x75, 0x31, 0x53, 0x3e, 0x01, 0x99, 0x0a, 0xa2, 0x24, 0xb3, 0xbd, 0xcb, 0x1a, 0x57, 0x8b, 0x16, - 0xaa, 0x45, 0xeb, 0x86, 0x6a, 0xa9, 0x67, 0xe8, 0xc6, 0x4f, 0xff, 0x5c, 0x97, 0x74, 0x16, 0xa1, - 0x34, 0xa0, 0xe0, 0x98, 0x98, 0x18, 0x3d, 0x4a, 0x1b, 0xdd, 0x3e, 0xc5, 0x96, 0xb8, 0x3a, 0x4b, - 0x88, 0x20, 0x56, 0xa4, 0x9e, 0xa3, 0x51, 0xdc, 0x64, 0x29, 0x1b, 0x50, 0x64, 0x8b, 0xf4, 0xbd, - 0xd1, 0xc8, 0x26, 0x06, 0xe3, 0x7d, 0x91, 0xf1, 0xbe, 0x44, 0xed, 0x0d, 0x66, 0xbe, 0x47, 0x2b, - 0x70, 0x0d, 0xb2, 0x96, 0x49, 0x4c, 0x0e, 0x49, 0x33, 0x48, 0x86, 0x1a, 0x98, 0xf3, 0x5d, 0x58, - 0x8e, 0x54, 0x87, 0x39, 0x24, 0xc3, 0x57, 0x99, 0x98, 0x19, 0xf0, 0x16, 0xac, 0xba, 0xe8, 0x90, - 0x18, 0x67, 0xd1, 0x59, 0x86, 0x56, 0xa8, 0xef, 0xe1, 0x74, 0xc4, 0xdb, 0xb0, 0xd4, 0x0f, 0xc9, - 0xe7, 0x58, 0x60, 0xd8, 0x42, 0x64, 0x65, 0xb0, 0xab, 0x90, 0x31, 0x7d, 0x9f, 0x03, 0x72, 0x0c, - 0x90, 0x36, 0x7d, 0x9f, 0xb9, 0x6e, 0xc2, 0x0a, 0x3b, 0x63, 0x80, 0xf0, 0xd8, 0x21, 0x62, 0x91, - 0x3c, 0xc3, 0x2c, 0x53, 0x87, 0xce, 0xed, 0x0c, 0xfb, 0x16, 0x14, 0xd0, 0x81, 0x6d, 0x21, 0xb7, - 0x8f, 0x38, 0xae, 0xc0, 0x70, 0xf9, 0xd0, 0xc8, 0x40, 0x37, 0xa0, 0xe8, 0x07, 0x9e, 0xef, 0x61, - 0x14, 0x18, 0xa6, 0x65, 0x05, 0x08, 0xe3, 0xd2, 0x12, 0x5f, 0x2f, 0xb4, 0xd7, 0xb8, 0x59, 0xb9, - 0x04, 0xa9, 0xc0, 0x1b, 0xbb, 0x56, 0xe9, 0x15, 0xa5, 0x2c, 0xa5, 0xf3, 0x19, 0x35, 0xf3, 0x3b, - 0xf2, 0x37, 0x67, 0x52, 0x5c, 0x82, 0x12, 0xc8, 0xdb, 0x26, 0x31, 0x95, 0x22, 0x24, 0xc9, 0x21, - 0x2e, 0x49, 0x6a, 0x72, 0x23, 0xaf, 0xd3, 0x61, 0xf5, 0x55, 0x02, 0xe4, 0x87, 0x1e, 0x41, 0xca, - 0x6d, 0x90, 0x69, 0x51, 0x99, 0x56, 0x97, 0xce, 0x53, 0x7f, 0xc7, 0x1e, 0xb8, 0xc8, 0xda, 0xc3, - 0x83, 0xee, 0x91, 0x8f, 0x74, 0x06, 0x8e, 0x89, 0x2f, 0x31, 0x25, 0xbe, 0xd5, 0x30, 0xbb, 0x64, - 0x3c, 0xb9, 0x1d, 0xc8, 0x44, 0x9a, 0x92, 0xff, 0x4b, 0x53, 0xcb, 0x54, 0x53, 0x54, 0xf1, 0xc2, - 0xa0, 0xa7, 0x7b, 0x42, 0x5a, 0x75, 0xc8, 0x46, 0x4f, 0x9d, 0xd0, 0xe6, 0xeb, 0xc9, 0x7b, 0x12, - 0xa6, 0xbc, 0x07, 0x2b, 0x91, 0x52, 0x22, 0xaa, 0xb9, 0x3e, 0x8b, 0x91, 0x23, 0xe4, 0x3a, 0x2e, - 0x42, 0x83, 0x3f, 0x57, 0x9c, 0xf4, 0x89, 0x08, 0x9b, 0xec, 0xdd, 0xba, 0x0e, 0x59, 0x6c, 0x0f, - 0x5c, 0x93, 0x8c, 0x03, 0x24, 0x74, 0x3a, 0x31, 0x54, 0x7f, 0x91, 0x60, 0x91, 0xeb, 0x3e, 0xc6, - 0x9b, 0x74, 0x3e, 0x6f, 0x89, 0x79, 0xbc, 0x25, 0x2f, 0xce, 0x5b, 0x0d, 0x20, 0x4a, 0x06, 0x97, - 0x64, 0x35, 0xb9, 0x91, 0xdb, 0xba, 0x36, 0xbb, 0x10, 0x4f, 0xb1, 0x63, 0x0f, 0xc4, 0xb5, 0x8e, - 0x05, 0x55, 0xff, 0x90, 0x20, 0x1b, 0xf9, 0x95, 0x1a, 0x14, 0xc2, 0xbc, 0x8c, 0x47, 0x8e, 0x39, - 0x10, 0xda, 0x59, 0x9b, 0x9b, 0xdc, 0x1d, 0xc7, 0x1c, 0xe8, 0x39, 0x91, 0x0f, 0x9d, 0x9c, 0x5f, - 0x87, 0xc4, 0x9c, 0x3a, 0x4c, 0x15, 0x3e, 0x79, 0xb1, 0xc2, 0x4f, 0x95, 0x48, 0x3e, 0x5b, 0xa2, - 0x9f, 0x13, 0x90, 0x69, 0xb3, 0x9b, 0x66, 0x3a, 0xff, 0xc7, 0x8d, 0xb8, 0x06, 0x59, 0xdf, 0x73, - 0x0c, 0xee, 0x91, 0x99, 0x27, 0xe3, 0x7b, 0x8e, 0x3e, 0x53, 0xf6, 0xd4, 0x1b, 0xba, 0x2e, 0x8b, - 0x6f, 0x80, 0xb5, 0xf4, 0x59, 0xd6, 0x02, 0xc8, 0x73, 0x2a, 0x44, 0xe7, 0xbb, 0x45, 0x39, 0x60, - 0xad, 0x54, 0x9a, 0xed, 0xd4, 0x3c, 0x6d, 0x8e, 0xd4, 0x05, 0x8e, 0x46, 0xf0, 0x46, 0x21, 0x9a, - 0x6f, 0x69, 0x9e, 0x2c, 0x75, 0x81, 0xab, 0x7e, 0x2f, 0x01, 0xec, 0x52, 0x66, 0xd9, 0x79, 0x69, - 0xcf, 0xc2, 0x2c, 0x05, 0x63, 0x6a, 0xe7, 0xca, 0xbc, 0xa2, 0x89, 0xfd, 0xf3, 0x38, 0x9e, 0x77, - 0x03, 0x0a, 0x13, 0x31, 0x62, 0x14, 0x26, 0x73, 0xce, 0x22, 0x51, 0x2b, 0xe9, 0x20, 0xa2, 0xe7, - 0x0f, 0x62, 0xb3, 0xea, 0xaf, 0x12, 0x64, 0x59, 0x4e, 0x7b, 0x88, 0x98, 0x53, 0x35, 0x94, 0x2e, - 0x5e, 0xc3, 0x35, 0x00, 0xbe, 0x0c, 0xb6, 0x9f, 0x20, 0xa1, 0xac, 0x2c, 0xb3, 0x74, 0xec, 0x27, - 0x48, 0xf9, 0x28, 0x22, 0x3c, 0xf9, 0xef, 0x84, 0x8b, 0x2b, 0x1d, 0xd2, 0x7e, 0x05, 0xd2, 0xee, - 0x78, 0x64, 0xd0, 0x96, 0x20, 0x73, 0xb5, 0xba, 0xe3, 0x51, 0xf7, 0x10, 0x57, 0xbf, 0x86, 0x74, - 0xf7, 0x90, 0x7d, 0x4c, 0x51, 0x89, 0x06, 0x9e, 0x27, 0x3a, 0x38, 0xff, 0x72, 0xca, 0x50, 0x03, - 0x6b, 0x58, 0x0a, 0xc8, 0xb4, 0x55, 0x87, 0x9f, 0x76, 0x74, 0xac, 0x68, 0xaf, 0xf9, 0x99, 0x26, - 0x7a, 0xd3, 0xcd, 0xdf, 0x24, 0xc8, 0xc5, 0xde, 0x07, 0xe5, 0x03, 0xb8, 0x54, 0xdf, 0xdd, 0x6f, - 0xdc, 0x37, 0x9a, 0xdb, 0xc6, 0x9d, 0xdd, 0xda, 0x5d, 0xe3, 0x41, 0xeb, 0x7e, 0x6b, 0xff, 0x8b, - 0x56, 0x71, 0xa1, 0x7c, 0xf9, 0xf8, 0x44, 0x55, 0x62, 0xd8, 0x07, 0xee, 0x63, 0xd7, 0xfb, 0xc6, - 0x55, 0x36, 0x61, 0x75, 0x3a, 0xa4, 0x56, 0xef, 0xec, 0xb4, 0xba, 0x45, 0xa9, 0x7c, 0xe9, 0xf8, - 0x44, 0x5d, 0x89, 0x45, 0xd4, 0x7a, 0x18, 0xb9, 0x64, 0x36, 0xa0, 0xb1, 0xbf, 0xb7, 0xd7, 0xec, - 0x16, 0x13, 0x33, 0x01, 0xe2, 0xc1, 0xbe, 0x01, 0x2b, 0xd3, 0x01, 0xad, 0xe6, 0x6e, 0x31, 0x59, - 0x56, 0x8e, 0x4f, 0xd4, 0xa5, 0x18, 0xba, 0x65, 0x3b, 0xe5, 0xcc, 0xb7, 0x3f, 0x54, 0x16, 0x7e, - 0xfa, 0xb1, 0x22, 0xd1, 0x93, 0x15, 0xa6, 0xde, 0x08, 0xe5, 0x7d, 0xb8, 0xd2, 0x69, 0xde, 0x6d, - 0xed, 0x6c, 0x1b, 0x7b, 0x9d, 0xbb, 0x46, 0xf7, 0xcb, 0xf6, 0x4e, 0xec, 0x74, 0xcb, 0xc7, 0x27, - 0x6a, 0x4e, 0x1c, 0x69, 0x1e, 0xba, 0xad, 0xef, 0x3c, 0xdc, 0xef, 0xee, 0x14, 0x25, 0x8e, 0x6e, - 0x07, 0xe8, 0xc0, 0x23, 0x88, 0xa1, 0x6f, 0xc1, 0xd5, 0x73, 0xd0, 0xd1, 0xc1, 0x56, 0x8e, 0x4f, - 0xd4, 0x42, 0x3b, 0x40, 0xfc, 0xfe, 0xb0, 0x08, 0x0d, 0x4a, 0xb3, 0x11, 0xfb, 0xed, 0xfd, 0x4e, - 0x6d, 0xb7, 0xa8, 0x96, 0x8b, 0xc7, 0x27, 0x6a, 0x3e, 0x7c, 0x0c, 0x29, 0x7e, 0x72, 0xb2, 0xfa, - 0xe7, 0xcf, 0x4e, 0x2b, 0xd2, 0xf3, 0xd3, 0x8a, 0xf4, 0xd7, 0x69, 0x45, 0x7a, 0xfa, 0xb2, 0xb2, - 0xf0, 0xfc, 0x65, 0x65, 0xe1, 0xf7, 0x97, 0x95, 0x85, 0xaf, 0x3e, 0x1e, 0xd8, 0x64, 0x38, 0xee, - 0x69, 0x7d, 0x6f, 0xb4, 0x19, 0xff, 0x03, 0x31, 0x19, 0xf2, 0x3f, 0x32, 0x67, 0xff, 0x5c, 0xf4, - 0x16, 0x99, 0xfd, 0xf6, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xa0, 0x63, 0x6b, 0x1d, 0x0d, - 0x00, 0x00, + 0x14, 0x8f, 0x6c, 0x39, 0xb6, 0x9f, 0xed, 0x44, 0xd1, 0xa4, 0xad, 0xeb, 0x36, 0x8e, 0xc6, 0x0c, + 0xe0, 0x16, 0x46, 0x29, 0x29, 0x43, 0xe1, 0xc0, 0xc1, 0x4e, 0xd2, 0xd6, 0xd3, 0xc4, 0x31, 0xb2, + 0x5b, 0x06, 0x2e, 0x1a, 0xd9, 0xda, 0xda, 0xa2, 0xb2, 0xa4, 0xd1, 0xae, 0x43, 0xd2, 0x33, 0x07, + 0xc6, 0xa7, 0x7e, 0x81, 0x9c, 0xe0, 0xc0, 0x9d, 0x2f, 0xc0, 0x70, 0xea, 0xb1, 0x37, 0xb8, 0x50, + 0x98, 0xf4, 0x52, 0xbe, 0x05, 0xb3, 0x7f, 0x64, 0xcb, 0x71, 0x02, 0x9d, 0x4c, 0x87, 0x8b, 0x67, + 0xf7, 0xbd, 0xdf, 0xdb, 0x7d, 0xfb, 0x7b, 0xbf, 0xdd, 0x27, 0xc3, 0x75, 0x82, 0x3c, 0x1b, 0x85, + 0x43, 0xc7, 0x23, 0x1b, 0xe4, 0x28, 0x40, 0x98, 0xff, 0xea, 0x41, 0xe8, 0x13, 0x5f, 0x55, 0xa6, + 0x5e, 0x9d, 0xd9, 0x4b, 0xab, 0x7d, 0xbf, 0xef, 0x33, 0xe7, 0x06, 0x1d, 0x71, 0x5c, 0x69, 0xbd, + 0xef, 0xfb, 0x7d, 0x17, 0x6d, 0xb0, 0x59, 0x77, 0xf4, 0x78, 0x83, 0x38, 0x43, 0x84, 0x89, 0x35, + 0x0c, 0x04, 0x60, 0x2d, 0xb6, 0x4d, 0x2f, 0x3c, 0x0a, 0x88, 0x4f, 0xb1, 0xfe, 0x63, 0xe1, 0x2e, + 0xc7, 0xdc, 0x07, 0x28, 0xc4, 0x8e, 0xef, 0xc5, 0xf3, 0x28, 0x69, 0x73, 0x59, 0x1e, 0x58, 0xae, + 0x63, 0x5b, 0xc4, 0x0f, 0x39, 0xa2, 0xf2, 0x19, 0x14, 0x5a, 0x56, 0x48, 0xda, 0x88, 0xdc, 0x47, + 0x96, 0x8d, 0x42, 0x75, 0x15, 0x52, 0xc4, 0x27, 0x96, 0x5b, 0x94, 0x34, 0xa9, 0x5a, 0x30, 0xf8, + 0x44, 0x55, 0x41, 0x1e, 0x58, 0x78, 0x50, 0x4c, 0x68, 0x52, 0x35, 0x6f, 0xb0, 0x71, 0x65, 0x00, + 0x32, 0x0d, 0xa5, 0x11, 0x8e, 0x67, 0xa3, 0xc3, 0x28, 0x82, 0x4d, 0xa8, 0xb5, 0x7b, 0x44, 0x10, + 0x16, 0x21, 0x7c, 0xa2, 0x7e, 0x0c, 0x29, 0x96, 0x7f, 0x31, 0xa9, 0x49, 0xd5, 0xdc, 0x66, 0x51, + 0x8f, 0x11, 0xc5, 0xcf, 0xa7, 0xb7, 0xa8, 0xbf, 0x2e, 0x3f, 0x7f, 0xb9, 0xbe, 0x60, 0x70, 0x70, + 0xc5, 0x85, 0x74, 0xdd, 0xf5, 0x7b, 0x4f, 0x1a, 0xdb, 0x93, 0x44, 0xa4, 0x69, 0x22, 0xea, 0x1e, + 0x2c, 0x07, 0x56, 0x48, 0x4c, 0x8c, 0x88, 0x39, 0x60, 0xa7, 0x60, 0x9b, 0xe6, 0x36, 0xd7, 0xf5, + 0xd3, 0x75, 0xd0, 0x67, 0x0e, 0x2b, 0x76, 0x29, 0x04, 0x71, 0x63, 0xe5, 0xbb, 0x14, 0x2c, 0x0a, + 0x32, 0x3e, 0x87, 0xb4, 0xa0, 0x95, 0x6d, 0x98, 0xdb, 0x5c, 0x8b, 0xaf, 0x28, 0x5c, 0xfa, 0x96, + 0xef, 0x61, 0xe4, 0xe1, 0x11, 0x16, 0xeb, 0x45, 0x31, 0xea, 0x7b, 0x90, 0xe9, 0x0d, 0x2c, 0xc7, + 0x33, 0x1d, 0x9b, 0x65, 0x94, 0xad, 0xe7, 0x4e, 0x5e, 0xae, 0xa7, 0xb7, 0xa8, 0xad, 0xb1, 0x6d, + 0xa4, 0x99, 0xb3, 0x61, 0xab, 0x97, 0x61, 0x71, 0x80, 0x9c, 0xfe, 0x80, 0x30, 0x5a, 0x92, 0x86, + 0x98, 0xa9, 0x9f, 0x82, 0x4c, 0x05, 0x51, 0x94, 0xd9, 0xde, 0x25, 0x9d, 0xab, 0x45, 0x8f, 0xd4, + 0xa2, 0x77, 0x22, 0xb5, 0xd4, 0x33, 0x74, 0xe3, 0x67, 0x7f, 0xae, 0x4b, 0x06, 0x8b, 0x50, 0xb7, + 0xa0, 0xe0, 0x5a, 0x98, 0x98, 0x5d, 0x4a, 0x1b, 0xdd, 0x3e, 0xc5, 0x96, 0xb8, 0x3a, 0x4f, 0x88, + 0x20, 0x56, 0xa4, 0x9e, 0xa3, 0x51, 0xdc, 0x64, 0xab, 0x55, 0x50, 0xd8, 0x22, 0x3d, 0x7f, 0x38, + 0x74, 0x88, 0xc9, 0x78, 0x5f, 0x64, 0xbc, 0x2f, 0x51, 0xfb, 0x16, 0x33, 0xdf, 0xa7, 0x15, 0xb8, + 0x06, 0x59, 0xdb, 0x22, 0x16, 0x87, 0xa4, 0x19, 0x24, 0x43, 0x0d, 0xcc, 0xb9, 0x0e, 0xb9, 0x03, + 0x9f, 0xa0, 0x10, 0x73, 0x77, 0x86, 0xb9, 0x81, 0x9b, 0x18, 0xa0, 0x0a, 0x8a, 0x87, 0x0e, 0x89, + 0x19, 0x47, 0x65, 0xf9, 0x3e, 0xd4, 0xfe, 0x68, 0x8a, 0x7c, 0x17, 0x96, 0x7a, 0x11, 0xd9, 0x1c, + 0x07, 0x0c, 0x57, 0x98, 0x58, 0x19, 0xec, 0x2a, 0x64, 0xac, 0x20, 0xe0, 0x80, 0x1c, 0x03, 0xa4, + 0xad, 0x20, 0x60, 0xae, 0x9b, 0xb0, 0xc2, 0xce, 0x14, 0x22, 0x3c, 0x72, 0x89, 0x58, 0x24, 0xcf, + 0x30, 0xcb, 0xd4, 0x61, 0x70, 0x3b, 0xc3, 0xbe, 0x03, 0x05, 0x74, 0xe0, 0xd8, 0xc8, 0xeb, 0x21, + 0x8e, 0x2b, 0x30, 0x5c, 0x3e, 0x32, 0x32, 0xd0, 0x0d, 0x50, 0x82, 0xd0, 0x0f, 0x7c, 0x8c, 0x42, + 0xd3, 0xb2, 0xed, 0x10, 0x61, 0x5c, 0x5c, 0xe2, 0xeb, 0x45, 0xf6, 0x1a, 0x37, 0xab, 0x97, 0x20, + 0x15, 0xfa, 0x23, 0xcf, 0x2e, 0xbe, 0xa6, 0x14, 0xa5, 0x0c, 0x3e, 0xa3, 0x66, 0x7e, 0x27, 0xfe, + 0xe6, 0xcc, 0x09, 0xd1, 0x17, 0x41, 0xde, 0xb6, 0x88, 0xa5, 0x2a, 0x90, 0x24, 0x87, 0xb8, 0x28, + 0x69, 0xc9, 0x6a, 0xde, 0xa0, 0xc3, 0xca, 0xeb, 0x04, 0xc8, 0x94, 0x14, 0xf5, 0x36, 0xc8, 0xb4, + 0x88, 0x4c, 0x9b, 0x4b, 0x67, 0xa9, 0xbd, 0xed, 0xf4, 0x3d, 0x64, 0xef, 0xe1, 0x7e, 0xe7, 0x28, + 0x40, 0x06, 0x03, 0xc7, 0xc4, 0x96, 0x98, 0x11, 0xdb, 0x6a, 0x94, 0x5d, 0x32, 0x9e, 0xdc, 0x0e, + 0x64, 0x26, 0x1a, 0x92, 0xff, 0x4b, 0x43, 0xcb, 0x54, 0x43, 0x54, 0xe1, 0xc2, 0x60, 0xa4, 0xbb, + 0x42, 0x4a, 0x75, 0xc8, 0x4e, 0x9e, 0x36, 0xa1, 0xc5, 0x37, 0x93, 0xf3, 0x34, 0x4c, 0xfd, 0x00, + 0x56, 0x26, 0xaf, 0xd7, 0x84, 0x6a, 0xae, 0x47, 0x65, 0xe2, 0x88, 0xb8, 0x7e, 0x1f, 0x96, 0xa7, + 0x60, 0xfe, 0x3c, 0x71, 0xd2, 0x97, 0x26, 0xe6, 0x06, 0x7b, 0xa7, 0xae, 0x43, 0x16, 0x3b, 0x7d, + 0xcf, 0x22, 0xa3, 0x10, 0x09, 0x6d, 0x4e, 0x0d, 0x95, 0x5f, 0x24, 0x58, 0xe4, 0x3a, 0x8f, 0xf1, + 0x26, 0x9d, 0xcd, 0x5b, 0xe2, 0x3c, 0xde, 0x92, 0x17, 0xe7, 0xad, 0x06, 0x30, 0x49, 0x06, 0x17, + 0x65, 0x2d, 0x59, 0xcd, 0x6d, 0x5e, 0x9b, 0x5f, 0x88, 0xa7, 0xd8, 0x76, 0xfa, 0xe2, 0x1a, 0xc7, + 0x82, 0x2a, 0x7f, 0x48, 0x90, 0x9d, 0xf8, 0xd5, 0x1a, 0x14, 0xa2, 0xbc, 0xcc, 0xc7, 0xae, 0xd5, + 0x17, 0xda, 0x59, 0x3b, 0x37, 0xb9, 0xbb, 0xae, 0xd5, 0x37, 0x72, 0x22, 0x1f, 0x3a, 0x39, 0xbb, + 0x0e, 0x89, 0x73, 0xea, 0x30, 0x53, 0xf8, 0xe4, 0xc5, 0x0a, 0x3f, 0x53, 0x22, 0xf9, 0x74, 0x89, + 0x7e, 0x4e, 0x40, 0xa6, 0xc5, 0x6e, 0x9a, 0xe5, 0xfe, 0x1f, 0x37, 0xe2, 0x1a, 0x64, 0x03, 0xdf, + 0x35, 0xb9, 0x47, 0x66, 0x9e, 0x4c, 0xe0, 0xbb, 0xc6, 0x5c, 0xd9, 0x53, 0x6f, 0xe9, 0xba, 0x2c, + 0xbe, 0x05, 0xd6, 0xd2, 0xa7, 0x59, 0x0b, 0x21, 0xcf, 0xa9, 0x10, 0x9d, 0xee, 0x16, 0xe5, 0x80, + 0xb5, 0x4e, 0x69, 0xbe, 0x33, 0xf3, 0xb4, 0x39, 0xd2, 0x10, 0x38, 0x1a, 0xc1, 0x1b, 0x83, 0x68, + 0xb6, 0xc5, 0xf3, 0x64, 0x69, 0x08, 0x5c, 0x65, 0x2c, 0x01, 0xec, 0x52, 0x66, 0xd9, 0x79, 0x69, + 0x8f, 0xc2, 0x2c, 0x05, 0x73, 0x66, 0xe7, 0xf2, 0x79, 0x45, 0x13, 0xfb, 0xe7, 0x71, 0x3c, 0xef, + 0x3b, 0x90, 0x65, 0x6d, 0x83, 0x36, 0x7f, 0x91, 0x48, 0x69, 0x7e, 0x01, 0xd6, 0x42, 0xda, 0x88, + 0x18, 0x99, 0x03, 0x31, 0xaa, 0xfc, 0x2a, 0x41, 0x96, 0xe5, 0xb1, 0x87, 0x88, 0x35, 0x53, 0x37, + 0xe9, 0xe2, 0x75, 0x5b, 0x03, 0xe0, 0xcb, 0x60, 0xe7, 0x29, 0x12, 0x6a, 0xca, 0x32, 0x4b, 0xdb, + 0x79, 0x8a, 0xd4, 0x4f, 0x26, 0x24, 0x27, 0xff, 0x9d, 0x64, 0x71, 0x8d, 0x23, 0xaa, 0xaf, 0x40, + 0xda, 0x1b, 0x0d, 0x4d, 0xda, 0x06, 0x64, 0xae, 0x50, 0x6f, 0x34, 0xec, 0x1c, 0xe2, 0xca, 0x37, + 0x90, 0xee, 0x1c, 0xb2, 0x0f, 0x26, 0x2a, 0xcb, 0xd0, 0xf7, 0x45, 0x97, 0xe6, 0x5f, 0x47, 0x19, + 0x6a, 0x60, 0x4d, 0x4a, 0x05, 0x99, 0xb6, 0xe3, 0xe8, 0xf3, 0x8d, 0x8e, 0x55, 0xfd, 0x0d, 0x3f, + 0xc5, 0x44, 0x3f, 0xba, 0xf9, 0x9b, 0x04, 0xb9, 0xd8, 0x9b, 0xa0, 0x7e, 0x04, 0x97, 0xea, 0xbb, + 0xfb, 0x5b, 0x0f, 0xcc, 0xc6, 0xb6, 0x79, 0x77, 0xb7, 0x76, 0xcf, 0x7c, 0xd8, 0x7c, 0xd0, 0xdc, + 0xff, 0xb2, 0xa9, 0x2c, 0x94, 0x2e, 0x8f, 0x8f, 0x35, 0x35, 0x86, 0x7d, 0xe8, 0x3d, 0xf1, 0xfc, + 0x6f, 0x3d, 0x75, 0x03, 0x56, 0x67, 0x43, 0x6a, 0xf5, 0xf6, 0x4e, 0xb3, 0xa3, 0x48, 0xa5, 0x4b, + 0xe3, 0x63, 0x6d, 0x25, 0x16, 0x51, 0xeb, 0x62, 0xe4, 0x91, 0xf9, 0x80, 0xad, 0xfd, 0xbd, 0xbd, + 0x46, 0x47, 0x49, 0xcc, 0x05, 0x88, 0x47, 0xfa, 0x06, 0xac, 0xcc, 0x06, 0x34, 0x1b, 0xbb, 0x4a, + 0xb2, 0xa4, 0x8e, 0x8f, 0xb5, 0xa5, 0x18, 0xba, 0xe9, 0xb8, 0xa5, 0xcc, 0xf7, 0x3f, 0x94, 0x17, + 0x7e, 0xfa, 0xb1, 0x2c, 0xd1, 0x93, 0x15, 0x66, 0xde, 0x05, 0xf5, 0x43, 0xb8, 0xd2, 0x6e, 0xdc, + 0x6b, 0xee, 0x6c, 0x9b, 0x7b, 0xed, 0x7b, 0x66, 0xe7, 0xab, 0xd6, 0x4e, 0xec, 0x74, 0xcb, 0xe3, + 0x63, 0x2d, 0x27, 0x8e, 0x74, 0x1e, 0xba, 0x65, 0xec, 0x3c, 0xda, 0xef, 0xec, 0x28, 0x12, 0x47, + 0xb7, 0x42, 0x44, 0x85, 0xc7, 0xd0, 0xb7, 0xe0, 0xea, 0x19, 0xe8, 0xc9, 0xc1, 0x56, 0xc6, 0xc7, + 0x5a, 0xa1, 0x15, 0x22, 0x7e, 0x67, 0x58, 0x84, 0x0e, 0xc5, 0xf9, 0x88, 0xfd, 0xd6, 0x7e, 0xbb, + 0xb6, 0xab, 0x68, 0x25, 0x65, 0x7c, 0xac, 0xe5, 0xa3, 0x07, 0x90, 0xe2, 0xa7, 0x27, 0xab, 0x7f, + 0xf1, 0xfc, 0xa4, 0x2c, 0xbd, 0x38, 0x29, 0x4b, 0x7f, 0x9d, 0x94, 0xa5, 0x67, 0xaf, 0xca, 0x0b, + 0x2f, 0x5e, 0x95, 0x17, 0x7e, 0x7f, 0x55, 0x5e, 0xf8, 0xfa, 0x4e, 0xdf, 0x21, 0x83, 0x51, 0x57, + 0xef, 0xf9, 0xc3, 0x8d, 0xf8, 0x9f, 0x84, 0xe9, 0x90, 0xff, 0x59, 0x39, 0xfd, 0x07, 0xa2, 0xbb, + 0xc8, 0xec, 0xb7, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x51, 0x60, 0x67, 0xe0, 0x01, 0x0d, 0x00, + 0x00, } func (m *PartSetHeader) Marshal() (dAtA []byte, err error) { @@ -1344,17 +1344,17 @@ func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x52 } - if len(m.NextValidatorsHash) > 0 { - i -= len(m.NextValidatorsHash) - copy(dAtA[i:], m.NextValidatorsHash) - i = encodeVarintTypes(dAtA, i, uint64(len(m.NextValidatorsHash))) + if len(m.NextVotersHash) > 0 { + i -= len(m.NextVotersHash) + copy(dAtA[i:], m.NextVotersHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.NextVotersHash))) i-- dAtA[i] = 0x4a } - if len(m.ValidatorsHash) > 0 { - i -= len(m.ValidatorsHash) - copy(dAtA[i:], m.ValidatorsHash) - i = encodeVarintTypes(dAtA, i, uint64(len(m.ValidatorsHash))) + if len(m.VotersHash) > 0 { + i -= len(m.VotersHash) + copy(dAtA[i:], m.VotersHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.VotersHash))) i-- dAtA[i] = 0x42 } @@ -1764,9 +1764,9 @@ func (m *LightBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.ValidatorSet != nil { + if m.VoterSet != nil { { - size, err := m.ValidatorSet.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.VoterSet.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1980,11 +1980,11 @@ func (m *Header) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = len(m.ValidatorsHash) + l = len(m.VotersHash) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = len(m.NextValidatorsHash) + l = len(m.NextVotersHash) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } @@ -2167,8 +2167,8 @@ func (m *LightBlock) Size() (n int) { l = m.SignedHeader.Size() n += 1 + l + sovTypes(uint64(l)) } - if m.ValidatorSet != nil { - l = m.ValidatorSet.Size() + if m.VoterSet != nil { + l = m.VoterSet.Size() n += 1 + l + sovTypes(uint64(l)) } return n @@ -2825,7 +2825,7 @@ func (m *Header) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ValidatorsHash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field VotersHash", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -2852,14 +2852,14 @@ func (m *Header) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.ValidatorsHash = append(m.ValidatorsHash[:0], dAtA[iNdEx:postIndex]...) - if m.ValidatorsHash == nil { - m.ValidatorsHash = []byte{} + m.VotersHash = append(m.VotersHash[:0], dAtA[iNdEx:postIndex]...) + if m.VotersHash == nil { + m.VotersHash = []byte{} } iNdEx = postIndex case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NextValidatorsHash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NextVotersHash", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -2886,9 +2886,9 @@ func (m *Header) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.NextValidatorsHash = append(m.NextValidatorsHash[:0], dAtA[iNdEx:postIndex]...) - if m.NextValidatorsHash == nil { - m.NextValidatorsHash = []byte{} + m.NextVotersHash = append(m.NextVotersHash[:0], dAtA[iNdEx:postIndex]...) + if m.NextVotersHash == nil { + m.NextVotersHash = []byte{} } iNdEx = postIndex case 10: @@ -4217,7 +4217,7 @@ func (m *LightBlock) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSet", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field VoterSet", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -4244,10 +4244,10 @@ func (m *LightBlock) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ValidatorSet == nil { - m.ValidatorSet = &ValidatorSet{} + if m.VoterSet == nil { + m.VoterSet = &VoterSet{} } - if err := m.ValidatorSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.VoterSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/proto/tendermint/types/types.proto b/proto/tendermint/types/types.proto index 127ba3bd7..e401be64e 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -70,11 +70,11 @@ message Header { bytes data_hash = 7; // transactions // hashes from the app output from the prev block - bytes validators_hash = 8; // validators for the current block - bytes next_validators_hash = 9; // validators for the next block - bytes consensus_hash = 10; // consensus params for current block - bytes app_hash = 11; // state after txs from the previous block - bytes last_results_hash = 12; // root hash of all results from the txs from the previous block + bytes voters_hash = 8; // voters for the current block + bytes next_voters_hash = 9; // voters for the next block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block // consensus info bytes evidence_hash = 13; // evidence included in the block @@ -144,8 +144,8 @@ message SignedHeader { } message LightBlock { - SignedHeader signed_header = 1; - tendermint.types.ValidatorSet validator_set = 2; + SignedHeader signed_header = 1; + tendermint.types.VoterSet voter_set = 2; } message BlockMeta { diff --git a/proto/tendermint/types/validator.pb.go b/proto/tendermint/types/validator.pb.go index f895d4934..74447facf 100644 --- a/proto/tendermint/types/validator.pb.go +++ b/proto/tendermint/types/validator.pb.go @@ -77,6 +77,58 @@ func (m *ValidatorSet) GetTotalVotingPower() int64 { return 0 } +type VoterSet struct { + Validators []*Validator `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators,omitempty"` + TotalVotingPower int64 `protobuf:"varint,2,opt,name=total_voting_power,json=totalVotingPower,proto3" json:"total_voting_power,omitempty"` +} + +func (m *VoterSet) Reset() { *m = VoterSet{} } +func (m *VoterSet) String() string { return proto.CompactTextString(m) } +func (*VoterSet) ProtoMessage() {} +func (*VoterSet) Descriptor() ([]byte, []int) { + return fileDescriptor_4e92274df03d3088, []int{1} +} +func (m *VoterSet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VoterSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VoterSet.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *VoterSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_VoterSet.Merge(m, src) +} +func (m *VoterSet) XXX_Size() int { + return m.Size() +} +func (m *VoterSet) XXX_DiscardUnknown() { + xxx_messageInfo_VoterSet.DiscardUnknown(m) +} + +var xxx_messageInfo_VoterSet proto.InternalMessageInfo + +func (m *VoterSet) GetValidators() []*Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *VoterSet) GetTotalVotingPower() int64 { + if m != nil { + return m.TotalVotingPower + } + return 0 +} + type Validator struct { Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` PubKey crypto.PublicKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` @@ -88,7 +140,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_4e92274df03d3088, []int{1} + return fileDescriptor_4e92274df03d3088, []int{2} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -154,7 +206,7 @@ func (m *SimpleValidator) Reset() { *m = SimpleValidator{} } func (m *SimpleValidator) String() string { return proto.CompactTextString(m) } func (*SimpleValidator) ProtoMessage() {} func (*SimpleValidator) Descriptor() ([]byte, []int) { - return fileDescriptor_4e92274df03d3088, []int{2} + return fileDescriptor_4e92274df03d3088, []int{3} } func (m *SimpleValidator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -199,6 +251,7 @@ func (m *SimpleValidator) GetVotingPower() int64 { func init() { proto.RegisterType((*ValidatorSet)(nil), "tendermint.types.ValidatorSet") + proto.RegisterType((*VoterSet)(nil), "tendermint.types.VoterSet") proto.RegisterType((*Validator)(nil), "tendermint.types.Validator") proto.RegisterType((*SimpleValidator)(nil), "tendermint.types.SimpleValidator") } @@ -206,30 +259,30 @@ func init() { func init() { proto.RegisterFile("tendermint/types/validator.proto", fileDescriptor_4e92274df03d3088) } var fileDescriptor_4e92274df03d3088 = []byte{ - // 353 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xc1, 0x6a, 0xf2, 0x40, - 0x14, 0x85, 0x33, 0x2a, 0xca, 0x3f, 0x0a, 0xbf, 0x1d, 0xba, 0x08, 0x56, 0xd2, 0xd4, 0x55, 0xa0, - 0x25, 0x01, 0x4b, 0xe9, 0xc2, 0x9d, 0x5b, 0x37, 0x36, 0x82, 0x8b, 0x6e, 0x42, 0x62, 0x86, 0x74, - 0x30, 0x7a, 0x87, 0xc9, 0xc4, 0x32, 0x6f, 0xd1, 0x67, 0xe9, 0x53, 0xb8, 0x74, 0xd9, 0x55, 0x29, - 0xfa, 0x22, 0xc5, 0xa4, 0x9a, 0x60, 0x5b, 0xba, 0xbb, 0xb9, 0xe7, 0xe4, 0x9e, 0x6f, 0xe0, 0x60, - 0x53, 0xd2, 0x65, 0x48, 0xc5, 0x82, 0x2d, 0xa5, 0x23, 0x15, 0xa7, 0x89, 0xb3, 0xf2, 0x63, 0x16, - 0xfa, 0x12, 0x84, 0xcd, 0x05, 0x48, 0x20, 0xed, 0xc2, 0x61, 0x67, 0x8e, 0xce, 0x79, 0x04, 0x11, - 0x64, 0xa2, 0xb3, 0x9f, 0x72, 0x5f, 0xa7, 0x5b, 0xba, 0x34, 0x13, 0x8a, 0x4b, 0x70, 0xe6, 0x54, - 0x25, 0xb9, 0xda, 0x53, 0xb8, 0x35, 0x3d, 0x1c, 0x9e, 0x50, 0x49, 0x06, 0x18, 0x1f, 0x83, 0x12, - 0x1d, 0x99, 0x55, 0xab, 0xd9, 0xbf, 0xb0, 0x4f, 0xa3, 0xec, 0xe3, 0x3f, 0x6e, 0xc9, 0x4e, 0x6e, - 0x30, 0x91, 0x20, 0xfd, 0xd8, 0x5b, 0x81, 0x64, 0xcb, 0xc8, 0xe3, 0xf0, 0x4c, 0x85, 0x5e, 0x35, - 0x91, 0x55, 0x75, 0xdb, 0x99, 0x32, 0xcd, 0x84, 0xf1, 0x7e, 0xdf, 0x7b, 0x45, 0xf8, 0xdf, 0xf1, - 0x0e, 0xd1, 0x71, 0xc3, 0x0f, 0x43, 0x41, 0x93, 0x7d, 0x2a, 0xb2, 0x5a, 0xee, 0xe1, 0x93, 0x0c, - 0x70, 0x83, 0xa7, 0x81, 0x37, 0xa7, 0x4a, 0xaf, 0x98, 0xc8, 0x6a, 0xf6, 0xbb, 0x65, 0x9e, 0xfc, - 0x49, 0xf6, 0x38, 0x0d, 0x62, 0x36, 0x1b, 0x51, 0x35, 0xac, 0xad, 0xdf, 0x2f, 0x35, 0xb7, 0xce, - 0xd3, 0x60, 0x44, 0x15, 0xb9, 0xc2, 0xad, 0x1f, 0x60, 0x9a, 0xab, 0x82, 0x83, 0x5c, 0xe3, 0x33, - 0x2e, 0x80, 0x43, 0x42, 0x85, 0xc7, 0x05, 0x03, 0xc1, 0xa4, 0xd2, 0x6b, 0x39, 0xf4, 0x41, 0x18, - 0x7f, 0xed, 0x7b, 0x73, 0xfc, 0x7f, 0xc2, 0x16, 0x3c, 0xa6, 0x05, 0xf9, 0x5d, 0xc1, 0x87, 0xfe, - 0xe6, 0xfb, 0x95, 0xac, 0xf2, 0x8d, 0x6c, 0xf8, 0xb0, 0xde, 0x1a, 0x68, 0xb3, 0x35, 0xd0, 0xc7, - 0xd6, 0x40, 0x2f, 0x3b, 0x43, 0xdb, 0xec, 0x0c, 0xed, 0x6d, 0x67, 0x68, 0x8f, 0xf7, 0x11, 0x93, - 0x4f, 0x69, 0x60, 0xcf, 0x60, 0xe1, 0x94, 0x9b, 0x52, 0x8c, 0x79, 0x0f, 0x4e, 0x5b, 0x14, 0xd4, - 0xb3, 0xfd, 0xed, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0x93, 0xca, 0x42, 0x7c, 0x60, 0x02, 0x00, - 0x00, + // 362 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x92, 0x31, 0x4f, 0xc2, 0x40, + 0x18, 0x86, 0x7b, 0x40, 0x40, 0x0f, 0x12, 0xf1, 0xe2, 0xd0, 0x20, 0xa9, 0xc8, 0x44, 0xa2, 0x69, + 0x13, 0x8c, 0x71, 0x60, 0x63, 0x65, 0xc1, 0x92, 0x30, 0xb8, 0x34, 0x2d, 0xbd, 0xd4, 0x0b, 0x85, + 0xef, 0x72, 0xbd, 0x62, 0xee, 0x5f, 0xf8, 0x5b, 0xfc, 0x15, 0x8c, 0x8c, 0x4e, 0xc6, 0xc0, 0x1f, + 0x31, 0xb4, 0x42, 0x1b, 0x94, 0x38, 0xb9, 0x5d, 0xbf, 0xf7, 0xed, 0xfb, 0x3e, 0xf9, 0xf2, 0xe1, + 0x96, 0xa4, 0x73, 0x9f, 0x8a, 0x19, 0x9b, 0x4b, 0x4b, 0x2a, 0x4e, 0x23, 0x6b, 0xe1, 0x86, 0xcc, + 0x77, 0x25, 0x08, 0x93, 0x0b, 0x90, 0x40, 0xea, 0x99, 0xc3, 0x4c, 0x1c, 0x8d, 0x8b, 0x00, 0x02, + 0x48, 0x44, 0x6b, 0xfb, 0x4a, 0x7d, 0x8d, 0x66, 0x2e, 0x69, 0x22, 0x14, 0x97, 0x60, 0x4d, 0xa9, + 0x8a, 0x52, 0xb5, 0xad, 0x70, 0x6d, 0xbc, 0x0b, 0x1e, 0x51, 0x49, 0x7a, 0x18, 0xef, 0x8b, 0x22, + 0x1d, 0xb5, 0x8a, 0x9d, 0x6a, 0xf7, 0xd2, 0x3c, 0xac, 0x32, 0xf7, 0xff, 0xd8, 0x39, 0x3b, 0xb9, + 0xc5, 0x44, 0x82, 0x74, 0x43, 0x67, 0x01, 0x92, 0xcd, 0x03, 0x87, 0xc3, 0x0b, 0x15, 0x7a, 0xb1, + 0x85, 0x3a, 0x45, 0xbb, 0x9e, 0x28, 0xe3, 0x44, 0x18, 0x6e, 0xe7, 0xed, 0x18, 0x9f, 0x8c, 0x41, + 0xd2, 0xff, 0xaa, 0x2d, 0x1c, 0xa9, 0x7d, 0x43, 0xf8, 0x74, 0x9f, 0x43, 0x74, 0x5c, 0x71, 0x7d, + 0x5f, 0xd0, 0x68, 0xdb, 0x8a, 0x3a, 0x35, 0x7b, 0xf7, 0x49, 0x7a, 0xb8, 0xc2, 0x63, 0xcf, 0x99, + 0x52, 0x95, 0x44, 0x55, 0xbb, 0xcd, 0x3c, 0x4f, 0xba, 0x49, 0x73, 0x18, 0x7b, 0x21, 0x9b, 0x0c, + 0xa8, 0xea, 0x97, 0x96, 0x1f, 0x57, 0x9a, 0x5d, 0xe6, 0xb1, 0x37, 0xa0, 0x8a, 0x5c, 0xe3, 0xda, + 0x2f, 0x3b, 0xa8, 0x2e, 0x32, 0x0e, 0x72, 0x83, 0xcf, 0xb9, 0x00, 0x0e, 0x11, 0x15, 0x0e, 0x17, + 0x0c, 0x04, 0x93, 0x4a, 0x2f, 0xa5, 0xd0, 0x3b, 0x61, 0xf8, 0x3d, 0x6f, 0x4f, 0xf1, 0xd9, 0x88, + 0xcd, 0x78, 0x48, 0x33, 0xf2, 0xfb, 0x8c, 0x0f, 0xfd, 0xcd, 0x77, 0x94, 0xac, 0xf0, 0x83, 0xac, + 0xff, 0xb8, 0x5c, 0x1b, 0x68, 0xb5, 0x36, 0xd0, 0xe7, 0xda, 0x40, 0xaf, 0x1b, 0x43, 0x5b, 0x6d, + 0x0c, 0xed, 0x7d, 0x63, 0x68, 0x4f, 0x0f, 0x01, 0x93, 0xcf, 0xb1, 0x67, 0x4e, 0x60, 0x66, 0xe5, + 0x0f, 0x34, 0x7b, 0xa6, 0xe7, 0x77, 0x78, 0xbc, 0x5e, 0x39, 0x99, 0xdf, 0x7d, 0x05, 0x00, 0x00, + 0xff, 0xff, 0x09, 0xac, 0x56, 0x63, 0xd7, 0x02, 0x00, 0x00, } func (m *ValidatorSet) Marshal() (dAtA []byte, err error) { @@ -274,6 +327,48 @@ func (m *ValidatorSet) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *VoterSet) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VoterSet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VoterSet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TotalVotingPower != 0 { + i = encodeVarintValidator(dAtA, i, uint64(m.TotalVotingPower)) + i-- + dAtA[i] = 0x10 + } + if len(m.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintValidator(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *Validator) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -393,6 +488,24 @@ func (m *ValidatorSet) Size() (n int) { return n } +func (m *VoterSet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Validators) > 0 { + for _, e := range m.Validators { + l = e.Size() + n += 1 + l + sovValidator(uint64(l)) + } + } + if m.TotalVotingPower != 0 { + n += 1 + sovValidator(uint64(m.TotalVotingPower)) + } + return n +} + func (m *Validator) Size() (n int) { if m == nil { return 0 @@ -539,6 +652,109 @@ func (m *ValidatorSet) Unmarshal(dAtA []byte) error { } return nil } +func (m *VoterSet) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VoterSet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VoterSet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthValidator + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthValidator + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validators = append(m.Validators, &Validator{}) + if err := m.Validators[len(m.Validators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalVotingPower", wireType) + } + m.TotalVotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalVotingPower |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipValidator(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthValidator + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Validator) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/tendermint/types/validator.proto b/proto/tendermint/types/validator.proto index 6680241e8..98f0efb3b 100644 --- a/proto/tendermint/types/validator.proto +++ b/proto/tendermint/types/validator.proto @@ -12,6 +12,11 @@ message ValidatorSet { int64 total_voting_power = 3; } +message VoterSet { + repeated Validator validators = 1; + int64 total_voting_power = 2; +} + message Validator { bytes address = 1; tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index 527b8a9b5..ebbde07b5 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -43,7 +43,7 @@ func newEvidence(t *testing.T, val *privval.FilePV, require.NoError(t, err) validator := types.NewValidator(val.Key.PubKey, 10) - valSet := types.NewValidatorSet([]*types.Validator{validator}) + valSet := types.NewVoterSet([]*types.Validator{validator}) return types.NewDuplicateVoteEvidence(vote, vote2, defaultTestTime, valSet) } diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 292609ccb..a34e2d94c 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -489,13 +489,13 @@ func (c *baseRPCClient) TxSearch( return result, nil } -func (c *baseRPCClient) Validators( +func (c *baseRPCClient) Voters( ctx context.Context, height *int64, page, perPage *int, -) (*ctypes.ResultValidators, error) { - result := new(ctypes.ResultValidators) +) (*ctypes.ResultVoters, error) { + result := new(ctypes.ResultVoters) params := make(map[string]interface{}) if page != nil { params["page"] = page @@ -506,7 +506,7 @@ func (c *baseRPCClient) Validators( if height != nil { params["height"] = height } - _, err := c.caller.Call(ctx, "validators", params, result) + _, err := c.caller.Call(ctx, "voters", params, result) if err != nil { return nil, err } diff --git a/rpc/client/interface.go b/rpc/client/interface.go index c6ff0fee2..120eea889 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -68,7 +68,7 @@ type SignClient interface { BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error) BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) - Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) + Voters(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultVoters, error) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error) diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index eb9cc485b..e02d98eb5 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -169,8 +169,8 @@ func (c *Local) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit return core.Commit(c.ctx, height) } -func (c *Local) Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) { - return core.Validators(c.ctx, height, page, perPage) +func (c *Local) Voters(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultVoters, error) { + return core.Voters(c.ctx, height, page, perPage) } func (c *Local) Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index ed911ec20..9f9d2a032 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -170,8 +170,8 @@ func (c Client) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit return core.Commit(&rpctypes.Context{}, height) } -func (c Client) Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) { - return core.Validators(&rpctypes.Context{}, height, page, perPage) +func (c Client) Voters(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultVoters, error) { + return core.Voters(&rpctypes.Context{}, height, page, perPage) } func (c Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { diff --git a/rpc/client/mocks/client.go b/rpc/client/mocks/client.go index 6a9008717..5a06d86a1 100644 --- a/rpc/client/mocks/client.go +++ b/rpc/client/mocks/client.go @@ -756,16 +756,16 @@ func (_m *Client) UnsubscribeAll(ctx context.Context, subscriber string) error { return r0 } -// Validators provides a mock function with given fields: ctx, height, page, perPage -func (_m *Client) Validators(ctx context.Context, height *int64, page *int, perPage *int) (*coretypes.ResultValidators, error) { +// Voters provides a mock function with given fields: ctx, height, page, perPage +func (_m *Client) Voters(ctx context.Context, height *int64, page *int, perPage *int) (*coretypes.ResultVoters, error) { ret := _m.Called(ctx, height, page, perPage) - var r0 *coretypes.ResultValidators - if rf, ok := ret.Get(0).(func(context.Context, *int64, *int, *int) *coretypes.ResultValidators); ok { + var r0 *coretypes.ResultVoters + if rf, ok := ret.Get(0).(func(context.Context, *int64, *int, *int) *coretypes.ResultVoters); ok { r0 = rf(ctx, height, page, perPage) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*coretypes.ResultValidators) + r0 = ret.Get(0).(*coretypes.ResultVoters) } } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 49f26b85f..8dbf8c966 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -173,11 +173,13 @@ func TestGenesisAndValidators(t *testing.T) { // get the current validators h := int64(1) - vals, err := c.Validators(context.Background(), &h, nil, nil) + vals, err := c.Voters(context.Background(), &h, nil, nil) require.Nil(t, err, "%d: %+v", i, err) require.Equal(t, 1, len(vals.Validators)) require.Equal(t, 1, vals.Count) require.Equal(t, 1, vals.Total) + require.Equal(t, 1, len(vals.VoterIndices)) + require.Equal(t, 0, vals.VoterIndices[0]) val := vals.Validators[0] // make sure the current set is also the genesis set diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 89b5d6a45..a52a12100 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -8,21 +8,30 @@ import ( "github.com/tendermint/tendermint/types" ) -// Validators gets the validator set at the given block height. +// Voters gets the validator set at the given block height. // // If no height is provided, it will fetch the latest validator set. Note the -// validators are sorted by their voting power - this is the canonical order -// for the validators in the set as used in computing their Merkle root. +// voters are sorted by their voting power - this is the canonical order +// for the voters in the set as used in computing their Merkle root. // // More: https://docs.tendermint.com/master/rpc/#/Info/validators -func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators, error) { +func Voters(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*ctypes.ResultVoters, error) { + return voters(ctx, heightPtr, pagePtr, perPagePtr, + func(height int64) (*types.ValidatorSet, *types.VoterSet, error) { + return env.StateStore.LoadValidators(height) + }) +} + +func voters(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int, + loadFunc func(height int64) (*types.ValidatorSet, *types.VoterSet, error)) (*ctypes.ResultVoters, error) { + // The latest validator that we know is the NextValidator of the last block. height, err := getHeight(latestUncommittedHeight(), heightPtr) if err != nil { return nil, err } - validators, err := env.StateStore.LoadValidators(height) + validators, voters, err := loadFunc(height) if err != nil { return nil, err } @@ -38,11 +47,17 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *in v := validators.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)] - return &ctypes.ResultValidators{ - BlockHeight: height, - Validators: v, - Count: len(v), - Total: totalCount}, nil + voterIndices := make([]int, 0) + for i := range v { + if j, _ := voters.GetByAddress(v[i].Address); j >= 0 { + voterIndices = append(voterIndices, j) + } + } + + return &ctypes.ResultVoters{ + BlockHeight: height, + Validators: v, + VoterIndices: voterIndices}, nil } // DumpConsensusState dumps consensus state. diff --git a/rpc/core/doc.go b/rpc/core/doc.go index 77ace4e2c..cf79a4d5f 100644 --- a/rpc/core/doc.go +++ b/rpc/core/doc.go @@ -25,7 +25,8 @@ Available endpoints: /health /unconfirmed_txs /unsafe_flush_mempool -/validators +/unsafe_stop_cpu_profiler +/voters Endpoints that require arguments: /abci_query?path=_&data=_&prove=_ diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 639a4be93..2fce14648 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -26,7 +26,7 @@ var Routes = map[string]*rpc.RPCFunc{ "check_tx": rpc.NewRPCFunc(CheckTx, "tx"), "tx": rpc.NewRPCFunc(Tx, "hash,prove"), "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"), - "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"), + "voters": rpc.NewRPCFunc(Voters, "height,page,per_page"), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""), "consensus_params": rpc.NewRPCFunc(ConsensusParams, "height"), diff --git a/rpc/core/status.go b/rpc/core/status.go index 72f50f546..9c5002778 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -75,7 +75,7 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { } func validatorAtHeight(h int64) *types.Validator { - vals, err := env.StateStore.LoadValidators(h) + vals, _, err := env.StateStore.LoadValidators(h) if err != nil { return nil } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 0327e6ee7..916fb2f86 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -122,10 +122,11 @@ type Peer struct { RemoteIP string `json:"remote_ip"` } -// Validators for a height. -type ResultValidators struct { - BlockHeight int64 `json:"block_height"` - Validators []*types.Validator `json:"validators"` +// ResultVoters for a height. +type ResultVoters struct { + BlockHeight int64 `json:"block_height"` + Validators []*types.Validator `json:"validators"` + VoterIndices []int `json:"voter_indices"` // Count of actual validators in this result Count int `json:"count"` // Total number of validators diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index a851ea408..1bf3dba97 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -758,10 +758,10 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /validators: + /voters: get: - summary: Get validator set at a specified height - operationId: validators + summary: Get voter set at a specified height + operationId: voters parameters: - in: query name: height @@ -789,14 +789,14 @@ paths: tags: - Info description: | - Get Validators. Validators are sorted by voting power. + Get Voters. Validators are sorted by voting power. responses: "200": description: Commit results. content: application/json: schema: - $ref: "#/components/schemas/ValidatorsResponse" + $ref: "#/components/schemas/VotersResponse" "500": description: Error content: @@ -1719,7 +1719,7 @@ components: type: boolean example: true type: object - ValidatorsResponse: + VotersResponse: type: object required: - "jsonrpc" @@ -1735,12 +1735,12 @@ components: result: required: - "block_height" - - "validators" + - "voters" properties: block_height: type: string example: "55" - validators: + voters: type: array items: $ref: "#/components/schemas/ValidatorPriority" @@ -1848,7 +1848,7 @@ components: - "step" - "start_time" - "commit_time" - - "validators" + - "voters" - "proposer" - "proposal" - "proposal_block" @@ -1862,7 +1862,7 @@ components: - "votes" - "commit_round" - "last_commit" - - "last_validators" + - "last_voters" - "triggered_timeout_precommit" properties: height: @@ -1880,11 +1880,11 @@ components: commit_time: type: string example: "2019-08-05T11:28:44.064658805Z" - validators: + voters: required: - - "validators" + - "voters" properties: - validators: + voters: type: array items: $ref: "#/components/schemas/ValidatorPriority" @@ -1947,11 +1947,11 @@ components: properties: {} type: object type: object - last_validators: + last_voters: required: - - "validators" + - "voters" properties: - validators: + voters: type: array items: $ref: "#/components/schemas/ValidatorPriority" @@ -2820,8 +2820,8 @@ components: - "last_block_id" - "last_commit_hash" - "data_hash" - - "validators_hash" - - "next_validators_hash" + - "voters_hash" + - "next_voters_hash" - "consensus_hash" - "app_hash" - "last_results_hash" @@ -2857,10 +2857,10 @@ components: data_hash: type: string example: "970886F99E77ED0D60DA8FCE0447C2676E59F2F77302B0C4AA10E1D02F18EF73" - validators_hash: + voters_hash: type: string example: "D658BFD100CA8025CFD3BECFE86194322731D387286FBD26E059115FD5F2BCA0" - next_validators_hash: + next_voters_hash: type: string example: "D658BFD100CA8025CFD3BECFE86194322731D387286FBD26E059115FD5F2BCA0" consensus_hash: @@ -2887,7 +2887,7 @@ components: properties: hash: type: string - example: "112BC173FD838FB68EB43476816CD7B4C6661B6884A9E357B417EE957E1CF8F7" + example: "D82C2734BB0E76C772A10994B210EF9D11505D1B98CB189D9CF7F9A5488672A5" parts: required: - "total" @@ -2898,5 +2898,5 @@ components: example: 1 hash: type: string - example: "38D4B26B5B725C4F13571EFE022C030390E4C33C8CF6F88EDD142EA769642DBD" + example: "CB02DCAA7FB46BF874052EC2273FD0B1F2CF2E1593298D9781E60FE9C3DB8638" type: object diff --git a/state/execution.go b/state/execution.go index 77f101628..044fe611b 100644 --- a/state/execution.go +++ b/state/execution.go @@ -342,7 +342,7 @@ func getBeginBlockValidatorInfo(block *types.Block, store Store, // Remember that the first LastCommit is intentionally empty, so it makes // sense for LastCommitInfo.Votes to also be empty. if block.Height > initialHeight { - lastValSet, err := store.LoadValidators(block.Height - 1) + _, lastVoterSet, err := store.LoadValidators(block.Height - 1) if err != nil { panic(err) } @@ -350,17 +350,17 @@ func getBeginBlockValidatorInfo(block *types.Block, store Store, // Sanity check that commit size matches validator set size - only applies // after first block. var ( - commitSize = block.LastCommit.Size() - valSetLen = len(lastValSet.Validators) + commitSize = block.LastCommit.Size() + voterSetLen = lastVoterSet.Size() ) - if commitSize != valSetLen { + if commitSize != voterSetLen { panic(fmt.Sprintf( "commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", - commitSize, valSetLen, block.Height, block.LastCommit.Signatures, lastValSet.Validators, + commitSize, voterSetLen, block.Height, block.LastCommit.Signatures, lastVoterSet.Voters, )) } - for i, val := range lastValSet.Validators { + for i, val := range lastVoterSet.Voters { commitSig := block.LastCommit.Signatures[i] voteInfos[i] = abci.VoteInfo{ Validator: types.TM2PB.Validator(val), @@ -410,7 +410,7 @@ func updateState( ) (State, error) { // Copy the valset so we can apply changes from EndBlock - // and update s.LastValidators and s.Validators. + // and update s.LastVoters and s.Validators. nValSet := state.NextValidators.Copy() // Update the validator set with the latest abciResponses. @@ -452,6 +452,8 @@ func updateState( return state, fmt.Errorf("error get proof of hash: %v", err) } + nextVoters := types.SelectVoter(nValSet, proofHash) + // NOTE: the AppHash has not been populated. // It will be filled on state.Save. return State{ @@ -463,8 +465,10 @@ func updateState( LastBlockTime: header.Time, LastProofHash: proofHash, NextValidators: nValSet, + NextVoters: nextVoters, Validators: state.NextValidators.Copy(), - LastValidators: state.Validators.Copy(), + Voters: state.NextVoters.Copy(), + LastVoters: state.Voters.Copy(), LastHeightValidatorsChanged: lastHeightValsChanged, ConsensusParams: nextParams, LastHeightConsensusParamsChanged: lastHeightParamsChanged, diff --git a/state/execution_test.go b/state/execution_test.go index 5f11bdc81..5a79f5365 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -101,7 +101,7 @@ func TestBeginBlockValidators(t *testing.T) { for _, tc := range testCases { lastCommit := types.NewCommit(1, 0, prevBlockID, tc.lastCommitSigs) - proposer := types.SelectProposer(state.Validators, state.LastProofHash, 1, 0) + proposer := state.Validators.SelectProposer(state.LastProofHash, 1, 0) message := state.MakeHashMessage(0) proof, _ := privVals[proposer.Address.String()].GenerateVRFProof(message) @@ -142,20 +142,20 @@ func TestBeginBlockByzantineValidators(t *testing.T) { privVal := privVals[state.Validators.Validators[0].Address.String()] blockID := makeBlockID([]byte("headerhash"), 1000, []byte("partshash")) header := &types.Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, - ChainID: state.ChainID, - Height: 10, - Time: defaultEvidenceTime, - LastBlockID: blockID, - LastCommitHash: crypto.CRandBytes(tmhash.Size), - DataHash: crypto.CRandBytes(tmhash.Size), - ValidatorsHash: state.Validators.Hash(), - NextValidatorsHash: state.Validators.Hash(), - ConsensusHash: crypto.CRandBytes(tmhash.Size), - AppHash: crypto.CRandBytes(tmhash.Size), - LastResultsHash: crypto.CRandBytes(tmhash.Size), - EvidenceHash: crypto.CRandBytes(tmhash.Size), - ProposerAddress: crypto.CRandBytes(crypto.AddressSize), + Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, + ChainID: state.ChainID, + Height: 10, + Time: defaultEvidenceTime, + LastBlockID: blockID, + LastCommitHash: crypto.CRandBytes(tmhash.Size), + DataHash: crypto.CRandBytes(tmhash.Size), + VotersHash: state.Validators.Hash(), + NextVotersHash: state.Validators.Hash(), + ConsensusHash: crypto.CRandBytes(tmhash.Size), + AppHash: crypto.CRandBytes(tmhash.Size), + LastResultsHash: crypto.CRandBytes(tmhash.Size), + EvidenceHash: crypto.CRandBytes(tmhash.Size), + ProposerAddress: crypto.CRandBytes(crypto.AddressSize), } // we don't need to worry about validating the evidence as long as they pass validate basic @@ -172,7 +172,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { Signature: crypto.CRandBytes(types.MaxSignatureSize), }}), }, - ValidatorSet: state.Validators, + VoterSet: state.Voters, }, CommonHeight: 8, ByzantineValidators: []*types.Validator{state.Validators.Validators[0]}, @@ -226,7 +226,6 @@ func TestBeginBlockByzantineValidators(t *testing.T) { } ev2 := types.LightClientAttackEvidence{} - //valSet := state.Validators testCases := []struct { desc string evidence []types.Evidence @@ -251,7 +250,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { lastCommit := types.NewCommit(9, 0, prevBlockID, commitSigs) for _, tc := range testCases { message := state.MakeHashMessage(0) - proposer := types.SelectProposer(state.Validators, state.LastProofHash, 1, 0) + proposer := state.Validators.SelectProposer(state.LastProofHash, 1, 0) proof, _ := privVals[proposer.Address.String()].GenerateVRFProof(message) block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, proposer.Address, 0, proof) block.Time = now @@ -260,7 +259,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { require.Nil(t, err, tc.desc) } - proposer := types.SelectProposer(state.Validators, state.LastProofHash, 12, 0) + proposer := state.Validators.SelectProposer(state.LastProofHash, 12, 0) privVal = privVals[proposer.Address.String()] block := makeBlockWithPrivVal(state, privVal, 12) @@ -269,7 +268,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} block.LastCommit, _ = makeValidCommit(11, state.LastBlockID, state.Validators, privVals) block.LastCommitHash = block.LastCommit.Hash() - block.Time = sm.MedianTime(block.LastCommit, state.LastValidators) + block.Time = sm.MedianTime(block.LastCommit, state.LastVoters) message := state.MakeHashMessage(block.Round) proof, _ := privVal.GenerateVRFProof(message) block.Proof = bytes.HexBytes(proof) diff --git a/state/export_test.go b/state/export_test.go index 56c3d764c..a7b98f30d 100644 --- a/state/export_test.go +++ b/state/export_test.go @@ -42,7 +42,7 @@ func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params tmproto // SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in // store.go, exported exclusively and explicitly for testing. -func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) error { +func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, proofHash []byte, valSet *types.ValidatorSet) error { stateStore := dbStore{db} - return stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet) + return stateStore.saveValidatorsInfo(height, lastHeightChanged, proofHash, valSet) } diff --git a/state/helpers_test.go b/state/helpers_test.go index 188fcf6de..9d9f286f6 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -40,7 +40,7 @@ func makeAndCommitGoodBlock( evidence []types.Evidence) (sm.State, types.BlockID, *types.Commit, error) { // A good block passes state, blockID, err := makeAndApplyGoodBlock(state, - privVals[types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address.String()], + privVals[state.Validators.SelectProposer(state.LastProofHash, height, 0).Address.String()], height, lastCommit, proposerAddr, blockExec, evidence) if err != nil { return state, types.BlockID{}, nil, err @@ -131,7 +131,7 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida for i := 1; i < height; i++ { s.LastBlockHeight++ - s.LastValidators = s.Validators.Copy() + s.LastVoters = s.Voters.Copy() if err := stateStore.Save(s); err != nil { panic(err) } diff --git a/state/mocks/store.go b/state/mocks/store.go index e9acc100f..89a7bfe5a 100644 --- a/state/mocks/store.go +++ b/state/mocks/store.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.1.0. DO NOT EDIT. +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. package mocks @@ -141,7 +141,7 @@ func (_m *Store) LoadFromDBOrGenesisFile(_a0 string) (state.State, error) { } // LoadValidators provides a mock function with given fields: _a0 -func (_m *Store) LoadValidators(_a0 int64) (*tenderminttypes.ValidatorSet, error) { +func (_m *Store) LoadValidators(_a0 int64) (*tenderminttypes.ValidatorSet, *tenderminttypes.VoterSet, error) { ret := _m.Called(_a0) var r0 *tenderminttypes.ValidatorSet @@ -153,14 +153,23 @@ func (_m *Store) LoadValidators(_a0 int64) (*tenderminttypes.ValidatorSet, error } } - var r1 error - if rf, ok := ret.Get(1).(func(int64) error); ok { + var r1 *tenderminttypes.VoterSet + if rf, ok := ret.Get(1).(func(int64) *tenderminttypes.VoterSet); ok { r1 = rf(_a0) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).(*tenderminttypes.VoterSet) + } } - return r0, r1 + var r2 error + if rf, ok := ret.Get(2).(func(int64) error); ok { + r2 = rf(_a0) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } // PruneStates provides a mock function with given fields: _a0, _a1 diff --git a/state/state.go b/state/state.go index 8a3aec2cf..d1f296366 100644 --- a/state/state.go +++ b/state/state.go @@ -59,7 +59,7 @@ type State struct { // vrf hash from proof LastProofHash []byte - // LastValidators is used to validate block.LastCommit. + // LastVoters is used to validate block.LastCommit. // Validators are persisted to the database separately every time they change, // so we can query for historical validator sets. // Note that if s.LastBlockHeight causes a valset change, @@ -67,7 +67,9 @@ type State struct { // Extra +1 due to nextValSet delay. NextValidators *types.ValidatorSet Validators *types.ValidatorSet - LastValidators *types.ValidatorSet + NextVoters *types.VoterSet + Voters *types.VoterSet + LastVoters *types.VoterSet LastHeightValidatorsChanged int64 // Consensus parameters used for validating blocks. @@ -101,8 +103,10 @@ func (state State) Copy() State { LastProofHash: state.LastProofHash, NextValidators: state.NextValidators.Copy(), + NextVoters: state.NextVoters.Copy(), Validators: state.Validators.Copy(), - LastValidators: state.LastValidators.Copy(), + Voters: state.Voters.Copy(), + LastVoters: state.LastVoters.Copy(), LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, ConsensusParams: state.ConsensusParams, @@ -167,11 +171,11 @@ func (state *State) ToProto() (*tmstate.State, error) { sm.NextValidators = nVals if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil - lVals, err := state.LastValidators.ToProto() + lVals, err := state.LastVoters.ToProto() if err != nil { return nil, err } - sm.LastValidators = lVals + sm.LastVoters = lVals } sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged @@ -218,13 +222,13 @@ func StateFromProto(pb *tmstate.State) (*State, error) { //nolint:golint state.NextValidators = nVals if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil - lVals, err := types.ValidatorSetFromProto(pb.LastValidators) + lVals, err := types.VoterSetFromProto(pb.LastVoters) if err != nil { return nil, err } - state.LastValidators = lVals + state.LastVoters = lVals } else { - state.LastValidators = types.NewValidatorSet(nil) + state.LastVoters = types.NewVoterSet(nil) } state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged @@ -262,14 +266,14 @@ func (state State) MakeBlock( if height == state.InitialHeight { timestamp = state.LastBlockTime // genesis time } else { - timestamp = MedianTime(commit, state.LastValidators) + timestamp = MedianTime(commit, state.LastVoters) } // Fill rest of header with state data. block.Header.Populate( state.Version.Consensus, state.ChainID, timestamp, state.LastBlockID, - state.Validators.Hash(), state.NextValidators.Hash(), + state.Voters.Hash(), state.NextVoters.Hash(), types.HashConsensusParams(state.ConsensusParams), state.AppHash, state.LastResultsHash, proposerAddress, round, @@ -283,7 +287,7 @@ func (state State) MakeBlock( // corresponding validator set. The computed time is always between timestamps of // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the // computed value. -func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { +func MedianTime(commit *types.Commit, voters *types.VoterSet) time.Time { weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures)) totalVotingPower := int64(0) @@ -291,7 +295,7 @@ func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time if commitSig.Absent() { continue } - _, validator := validators.GetByAddress(commitSig.ValidatorAddress) + _, validator := voters.GetByAddress(commitSig.ValidatorAddress) // If there's no condition, TestValidateBlockCommit panics; not needed normally. if validator != nil { totalVotingPower += validator.VotingPower @@ -363,8 +367,10 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { LastProofHash: genDoc.Hash(), NextValidators: nextValidatorSet, + NextVoters: types.SelectVoter(nextValidatorSet, genDoc.Hash()), Validators: validatorSet, - LastValidators: types.NewValidatorSet(nil), + Voters: types.ToVoterAll(validatorSet), + LastVoters: &types.VoterSet{}, LastHeightValidatorsChanged: 1, ConsensusParams: *genDoc.ConsensusParams, diff --git a/state/state_test.go b/state/state_test.go index 0f9aa9be7..72fedf2b0 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -55,7 +55,7 @@ func TestStateCopy(t *testing.T) { stateCopy, state)) stateCopy.LastBlockHeight++ - stateCopy.LastValidators = state.Validators + stateCopy.LastVoters = state.Voters assert.False(state.Equals(stateCopy), fmt.Sprintf(`expected states to be different. got same %v`, state)) } @@ -81,7 +81,7 @@ func TestStateSaveLoad(t *testing.T) { assert := assert.New(t) state.LastBlockHeight++ - state.LastValidators = state.Validators + state.LastVoters = state.Voters err := stateStore.Save(state) require.NoError(t, err) @@ -220,16 +220,16 @@ func TestValidatorSimpleSaveLoad(t *testing.T) { statestore := sm.NewStore(stateDB) // Can't load anything for height 0. - _, err := statestore.LoadValidators(0) + _, _, err := statestore.LoadValidators(0) assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0") // Should be able to load for height 1. - v, err := statestore.LoadValidators(1) + _, v, err := statestore.LoadValidators(1) assert.Nil(err, "expected no err at height 1") assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") // Should be able to load for height 2. - v, err = statestore.LoadValidators(2) + _, v, err = statestore.LoadValidators(2) assert.Nil(err, "expected no err at height 2") assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match") @@ -238,9 +238,9 @@ func TestValidatorSimpleSaveLoad(t *testing.T) { nextHeight := state.LastBlockHeight + 1 err = statestore.Save(state) require.NoError(t, err) - vp0, err := statestore.LoadValidators(nextHeight + 0) + _, vp0, err := statestore.LoadValidators(nextHeight + 0) assert.Nil(err, "expected no err") - vp1, err := statestore.LoadValidators(nextHeight + 1) + _, vp1, err := statestore.LoadValidators(nextHeight + 1) assert.Nil(err, "expected no err") assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match") @@ -294,7 +294,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) { } for i, power := range testCases { - v, err := stateStore.LoadValidators(int64(i + 1 + 1)) // +1 because vset changes delayed by 1 block. + _, v, err := stateStore.LoadValidators(int64(i + 1 + 1)) // +1 because vset changes delayed by 1 block. assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i)) assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size()) _, val := v.GetByIndex(0) @@ -396,15 +396,16 @@ func genValSetWithPowers(powers []int64) *types.ValidatorSet { // test a proposer appears as frequently as expected func testProposerFreq(t *testing.T, caseNum int, valSet *types.ValidatorSet) { - N := valSet.Size() - totalPower := valSet.TotalVotingPower() + voterSet := types.ToVoterAll(valSet) + N := voterSet.Size() + totalPower := voterSet.TotalVotingPower() // run the proposer selection and track frequencies runMult := 1 runs := int(totalPower) * runMult freqs := make([]int, N) for i := 0; i < runs; i++ { - prop := types.SelectProposer(valSet, []byte{}, 1, int32(i)) + prop := valSet.SelectProposer([]byte{}, 1, int32(i)) idx, _ := valSet.GetByAddress(prop.Address) freqs[idx]++ valSet.IncrementProposerPriority(1) @@ -559,7 +560,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { state.NextValidators = state.Validators // we only have one validator: assert.Equal(t, val1PubKey.Address(), - types.SelectProposer(state.Validators, []byte{}, state.LastBlockHeight+1, 0).Address) + state.Validators.SelectProposer([]byte{}, state.LastBlockHeight+1, 0).Address) block := makeBlock(state, state.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} @@ -759,7 +760,7 @@ func TestLargeGenesisValidator(t *testing.T) { // -> no change in ProposerPrio (stays zero): assert.EqualValues(t, oldState.NextValidators, updatedState.NextValidators) assert.EqualValues(t, 0, - types.SelectProposer(updatedState.NextValidators, []byte{}, block.Height, 0).ProposerPriority) + updatedState.NextValidators.SelectProposer([]byte{}, block.Height, 0).ProposerPriority) oldState = updatedState } @@ -867,8 +868,8 @@ func TestLargeGenesisValidator(t *testing.T) { blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(testPartSize).Header()} curState, err = sm.UpdateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) - if !bytes.Equal(types.SelectProposer(curState.Validators, []byte{}, int64(count), 0).Address, - types.SelectProposer(curState.NextValidators, []byte{}, int64(count+1), 0).Address) { + if !bytes.Equal(curState.Validators.SelectProposer([]byte{}, int64(count), 0).Address, + curState.NextValidators.SelectProposer([]byte{}, int64(count+1), 0).Address) { isProposerUnchanged = false } count++ @@ -910,19 +911,17 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { t.Cleanup(func() { tearDown(t) }) stateStore := sm.NewStore(stateDB) state.Validators = genValSet(valSetSize) - types.SelectProposer(state.Validators, []byte{}, 1, 0) + state.Validators.SelectProposer([]byte{}, 1, 0) state.NextValidators = state.Validators.Copy() - types.SelectProposer(state.NextValidators, []byte{}, 2, 0) + state.NextValidators.SelectProposer([]byte{}, 2, 0) err := stateStore.Save(state) require.NoError(t, err) nextHeight := state.LastBlockHeight + 1 - - v0, err := stateStore.LoadValidators(nextHeight) + v0, _, err := stateStore.LoadValidators(nextHeight) assert.Nil(t, err) acc0 := v0.Validators[0].ProposerPriority - - v1, err := stateStore.LoadValidators(nextHeight + 1) + v1, _, err := stateStore.LoadValidators(nextHeight + 1) assert.Nil(t, err) acc1 := v1.Validators[0].ProposerPriority @@ -938,9 +937,9 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { stateStore := sm.NewStore(stateDB) require.Equal(t, int64(0), state.LastBlockHeight) state.Validators = genValSet(valSetSize) - types.SelectProposer(state.Validators, []byte{}, 1, 0) + state.Validators.SelectProposer([]byte{}, 1, 0) state.NextValidators = state.Validators.Copy() - types.SelectProposer(state.NextValidators, []byte{}, 2, 0) + state.NextValidators.SelectProposer([]byte{}, 2, 0) err := stateStore.Save(state) require.NoError(t, err) @@ -962,7 +961,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { require.NoError(t, err) // Load nextheight, it should be the oldpubkey. - v0, err := stateStore.LoadValidators(nextHeight) + v0, _, err := stateStore.LoadValidators(nextHeight) assert.Nil(t, err) assert.Equal(t, valSetSize, v0.Size()) index, val := v0.GetByAddress(pubkeyOld.Address()) @@ -972,7 +971,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { } // Load nextheight+1, it should be the new pubkey. - v1, err := stateStore.LoadValidators(nextHeight + 1) + v1, _, err := stateStore.LoadValidators(nextHeight + 1) assert.Nil(t, err) assert.Equal(t, valSetSize, v1.Size()) index, val = v1.GetByAddress(pubkey.Address()) diff --git a/state/store.go b/state/store.go index 550c2cbff..c47bc88f7 100644 --- a/state/store.go +++ b/state/store.go @@ -55,7 +55,7 @@ type Store interface { // Load loads the current state of the blockchain Load() (State, error) // LoadValidators loads the validator set at a given height - LoadValidators(int64) (*types.ValidatorSet, error) + LoadValidators(int64) (*types.ValidatorSet, *types.VoterSet, error) // LoadABCIResponses loads the abciResponse for a given height LoadABCIResponses(int64) (*tmstate.ABCIResponses, error) // LoadConsensusParams loads the consensus params for a given height @@ -163,12 +163,12 @@ func (store dbStore) save(state State, key []byte) error { nextHeight = state.InitialHeight // This extra logic due to Tendermint validator set changes being delayed 1 block. // It may get overwritten due to InitChain validator updates. - if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil { + if err := store.saveValidatorsInfo(nextHeight, nextHeight, []byte{}, state.Validators); err != nil { return err } } // Save next validators. - if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil { + if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.LastProofHash, state.NextValidators); err != nil { return err } @@ -184,24 +184,26 @@ func (store dbStore) save(state State, key []byte) error { return nil } -// BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. +// Bootstrap saves a new state, used e.g. by state sync when starting from non-zero height. func (store dbStore) Bootstrap(state State) error { height := state.LastBlockHeight + 1 if height == 1 { height = state.InitialHeight } - if height > 1 && !state.LastValidators.IsNilOrEmpty() { - if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators); err != nil { + if height > 1 && !state.LastVoters.IsNilOrEmpty() { + // TODO 🏺Can apply empty bytes for the ProofHash corresponding to LastValidators? and LastVoters as LastValidators? + vals := types.NewValidatorSet(state.LastVoters.Voters) + if err := store.saveValidatorsInfo(height-1, height-1, []byte{}, vals); err != nil { return err } } - if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil { + if err := store.saveValidatorsInfo(height, height, []byte{}, state.Validators); err != nil { return err } - if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil { + if err := store.saveValidatorsInfo(height+1, height+1, state.LastProofHash, state.NextValidators); err != nil { return err } @@ -260,7 +262,7 @@ func (store dbStore) PruneStates(from int64, to int64) error { if keepVals[h] { v, err := loadValidatorsInfo(store.db, h) if err != nil || v.ValidatorSet == nil { - vip, err := store.LoadValidators(h) + vip, _, err := store.LoadValidators(h) if err != nil { return err } @@ -414,18 +416,19 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI //----------------------------------------------------------------------------- -// LoadValidators loads the ValidatorSet for a given height. +// LoadValidators loads the VoterSet for a given height. // Returns ErrNoValSetForHeight if the validator set can't be found for this height. -func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) { +func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, *types.VoterSet, error) { valInfo, err := loadValidatorsInfo(store.db, height) if err != nil { - return nil, ErrNoValSetForHeight{height} + return nil, nil, ErrNoValSetForHeight{height} } if valInfo.ValidatorSet == nil { + proofHash := valInfo.ProofHash // store proof hash of the height lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight) if err != nil || valInfo2.ValidatorSet == nil { - return nil, + return nil, nil, fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w", lastStoredHeight, height, @@ -435,25 +438,25 @@ func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) { vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet) if err != nil { - return nil, err + return nil, nil, err } vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate vi2, err := vs.ToProto() if err != nil { - return nil, err + return nil, nil, err } valInfo2.ValidatorSet = vi2 valInfo = valInfo2 + valInfo.ProofHash = proofHash // reload proof again } vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet) if err != nil { - return nil, err + return nil, nil, err } - - return vip, nil + return vip, types.SelectVoter(vip, valInfo.ProofHash), nil } func lastStoredHeightFor(height, lastHeightChanged int64) int64 { @@ -489,12 +492,13 @@ func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error // `height` is the effective height for which the validator is responsible for // signing. It should be called from s.Save(), right before the state itself is // persisted. -func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error { +func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, proofHash []byte, valSet *types.ValidatorSet) error { if lastHeightChanged > height { return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height") } valInfo := &tmstate.ValidatorsInfo{ LastHeightChanged: lastHeightChanged, + ProofHash: proofHash, } // Only persist validator set if it was updated or checkpoint height (see // valSetCheckpointInterval) is reached. diff --git a/state/store_test.go b/state/store_test.go index 91431b5e2..c82dc0f70 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -28,20 +28,20 @@ func TestStoreLoadValidators(t *testing.T) { vals := types.NewValidatorSet([]*types.Validator{val}) // 1) LoadValidators loads validators using a height where they were last changed - err := sm.SaveValidatorsInfo(stateDB, 1, 1, vals) + err := sm.SaveValidatorsInfo(stateDB, 1, 1, []byte{}, vals) require.NoError(t, err) - err = sm.SaveValidatorsInfo(stateDB, 2, 1, vals) + err = sm.SaveValidatorsInfo(stateDB, 2, 1, []byte{}, vals) require.NoError(t, err) - loadedVals, err := stateStore.LoadValidators(2) + loadedVals, _, err := stateStore.LoadValidators(2) require.NoError(t, err) assert.NotZero(t, loadedVals.Size()) // 2) LoadValidators loads validators using a checkpoint height - err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals) + err = sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, []byte{}, vals) require.NoError(t, err) - loadedVals, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval) + loadedVals, _, err = stateStore.LoadValidators(sm.ValSetCheckpointInterval) require.NoError(t, err) assert.NotZero(t, loadedVals.Size()) } @@ -61,22 +61,23 @@ func BenchmarkLoadValidators(b *testing.B) { } state.Validators = genValSet(valSetSize) - types.SelectProposer(state.Validators, []byte{}, 1, 0) + state.Validators.SelectProposer([]byte{}, 1, 0) state.NextValidators = state.Validators.Copy() - types.SelectProposer(state.NextValidators, []byte{}, 2, 0) + state.NextValidators.SelectProposer([]byte{}, 2, 0) + // state.Validators.SelectProposer([]byte{}, 2, 0) err = stateStore.Save(state) require.NoError(b, err) for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ... i := i if err := sm.SaveValidatorsInfo(stateDB, - int64(i), state.LastHeightValidatorsChanged, state.NextValidators); err != nil { + int64(i), state.LastHeightValidatorsChanged, []byte{}, state.NextValidators); err != nil { b.Fatal(err) } b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) { for n := 0; n < b.N; n++ { - _, err := stateStore.LoadValidators(int64(i)) + _, _, err := stateStore.LoadValidators(int64(i)) if err != nil { b.Fatal(err) } @@ -142,7 +143,7 @@ func TestPruneStates(t *testing.T) { } if state.LastBlockHeight >= 1 { - state.LastValidators = state.Validators + state.LastVoters = state.Voters } err := stateStore.Save(state) @@ -171,10 +172,11 @@ func TestPruneStates(t *testing.T) { expectABCI := sliceToMap(tc.expectABCI) for h := int64(1); h <= tc.makeHeights; h++ { - vals, err := stateStore.LoadValidators(h) + vals, votes, err := stateStore.LoadValidators(h) if expectVals[h] { require.NoError(t, err, "validators height %v", h) require.NotNil(t, vals) + require.NotNil(t, votes) } else { require.Error(t, err, "validators height %v", h) require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err) diff --git a/state/validation.go b/state/validation.go index 6806762e2..54945c99d 100644 --- a/state/validation.go +++ b/state/validation.go @@ -72,16 +72,16 @@ func validateBlock(state State, round int32, block *types.Block) error { block.LastResultsHash, ) } - if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) { - return fmt.Errorf("wrong Block.Header.ValidatorsHash. Expected %X, got %v", - state.Validators.Hash(), - block.ValidatorsHash, + if !bytes.Equal(block.VotersHash, state.Voters.Hash()) { + return fmt.Errorf("wrong Block.Header.VotersHash. Expected %X, got %v", + state.Voters.Hash(), + block.VotersHash, ) } - if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) { - return fmt.Errorf("wrong Block.Header.NextValidatorsHash. Expected %X, got %v", - state.NextValidators.Hash(), - block.NextValidatorsHash, + if !bytes.Equal(block.NextVotersHash, state.NextVoters.Hash()) { + return fmt.Errorf("wrong Block.Header.NextVotersHash. Expected %X, got %v", + state.NextVoters.Hash(), + block.NextVotersHash, ) } @@ -92,7 +92,7 @@ func validateBlock(state State, round int32, block *types.Block) error { } } else { // LastCommit.Signatures length is checked in VerifyCommit. - if err := state.LastValidators.VerifyCommit( + if err := state.LastVoters.VerifyCommit( state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit); err != nil { return err } @@ -122,7 +122,7 @@ func validateBlock(state State, round int32, block *types.Block) error { state.LastBlockTime, ) } - medianTime := MedianTime(block.LastCommit, state.LastValidators) + medianTime := MedianTime(block.LastCommit, state.LastVoters) if !block.Time.Equal(medianTime) { return fmt.Errorf("invalid block time. Expected %v, got %v", medianTime, @@ -151,10 +151,10 @@ func validateBlock(state State, round int32, block *types.Block) error { // validate proposer if !bytes.Equal(block.ProposerAddress.Bytes(), - types.SelectProposer(state.Validators, state.LastProofHash, block.Height, block.Round).Address.Bytes()) { + state.Validators.SelectProposer(state.LastProofHash, block.Height, block.Round).Address.Bytes()) { return fmt.Errorf("block.ProposerAddress, %X, is not the proposer %X", block.ProposerAddress, - types.SelectProposer(state.Validators, state.LastProofHash, block.Height, block.Round).Address, + state.Validators.SelectProposer(state.LastProofHash, block.Height, block.Round).Address, ) } diff --git a/state/validation_test.go b/state/validation_test.go index fd99aaf2b..d53cd5494 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -60,8 +60,8 @@ func TestValidateBlockHeader(t *testing.T) { {"LastCommitHash wrong", func(block *types.Block) { block.LastCommitHash = wrongHash }}, {"DataHash wrong", func(block *types.Block) { block.DataHash = wrongHash }}, - {"ValidatorsHash wrong", func(block *types.Block) { block.ValidatorsHash = wrongHash }}, - {"NextValidatorsHash wrong", func(block *types.Block) { block.NextValidatorsHash = wrongHash }}, + {"VotersHash wrong", func(block *types.Block) { block.VotersHash = wrongHash }}, + {"NextVotersHash wrong", func(block *types.Block) { block.NextVotersHash = wrongHash }}, {"ConsensusHash wrong", func(block *types.Block) { block.ConsensusHash = wrongHash }}, {"AppHash wrong", func(block *types.Block) { block.AppHash = wrongHash }}, {"LastResultsHash wrong", func(block *types.Block) { block.LastResultsHash = wrongHash }}, @@ -73,7 +73,7 @@ func TestValidateBlockHeader(t *testing.T) { // Build up state for multiple heights for height := int64(1); height < validationTestsStopHeight; height++ { - proposerAddr := types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address + proposerAddr := state.Validators.SelectProposer(state.LastProofHash, height, 0).Address /* Invalid blocks don't pass */ @@ -114,10 +114,10 @@ func TestValidateBlockCommit(t *testing.T) { badPrivVal := types.NewMockPV() for height := int64(1); height < validationTestsStopHeight; height++ { - proposerAddr := types.SelectProposer(state.Validators, []byte{}, height, 0).Address + proposerAddr := state.Validators.SelectProposer([]byte{}, height, 0).Address if height > 1 { /* - #2589: ensure state.LastValidators.VerifyCommit fails here + #2589: ensure state.LastVoters.VerifyCommit fails here */ // should be height-1 instead of height wrongHeightVote, err := types.MakeVote( @@ -143,7 +143,7 @@ func TestValidateBlockCommit(t *testing.T) { require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) /* - #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() + #2589: test len(block.LastCommit.Signatures) == state.LastVoters.Size() */ block, _ = state.MakeBlock(height, makeTxs(height), wrongSigsCommit, nil, proposerAddr, 0, proof) err = blockExec.ValidateBlock(state, 0, block) @@ -237,7 +237,7 @@ func TestValidateBlockEvidence(t *testing.T) { lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) for height := int64(1); height < validationTestsStopHeight; height++ { - proposerAddr := types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address + proposerAddr := state.Validators.SelectProposer(state.LastProofHash, height, 0).Address maxBytesEvidence := state.ConsensusParams.Evidence.MaxBytes if height > 1 { /* diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index c89052dbd..fdfb7a9ee 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -164,9 +164,11 @@ func (s *lightClientStateProvider) State(ctx context.Context, height uint64) (sm state.LastBlockID = lastLightBlock.Commit.BlockID state.AppHash = currentLightBlock.AppHash state.LastResultsHash = currentLightBlock.LastResultsHash - state.LastValidators = lastLightBlock.ValidatorSet + state.LastVoters = lastLightBlock.VoterSet state.Validators = currentLightBlock.ValidatorSet + state.Voters = currentLightBlock.VoterSet state.NextValidators = nextLightBlock.ValidatorSet + state.NextVoters = nextLightBlock.VoterSet state.LastHeightValidatorsChanged = nextLightBlock.Height // We'll also need to fetch consensus params via RPC, using light client verification. diff --git a/statesync/syncer_test.go b/statesync/syncer_test.go index fbe901beb..490770236 100644 --- a/statesync/syncer_test.go +++ b/statesync/syncer_test.go @@ -60,9 +60,11 @@ func TestSyncer_SyncAny(t *testing.T) { LastResultsHash: []byte("last_results_hash"), AppHash: []byte("app_hash"), - LastValidators: &types.ValidatorSet{}, + LastVoters: &types.VoterSet{}, Validators: &types.ValidatorSet{}, + Voters: &types.VoterSet{}, NextValidators: &types.ValidatorSet{}, + NextVoters: &types.VoterSet{}, ConsensusParams: *types.DefaultConsensusParams(), LastHeightConsensusParamsChanged: 1, diff --git a/store/store_test.go b/store/store_test.go index 8f3dffbaf..7ea0d3d6d 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -51,7 +51,7 @@ func makeTxs(height int64) (txs []types.Tx) { func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, - types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address, 0, nil) + state.Validators.SelectProposer(state.LastProofHash, height, 0).Address, 0, nil) return block } diff --git a/test/e2e/tests/validator_test.go b/test/e2e/tests/validator_test.go index 2af9e2ead..c92fd6ba0 100644 --- a/test/e2e/tests/validator_test.go +++ b/test/e2e/tests/validator_test.go @@ -40,7 +40,7 @@ func TestValidator_Sets(t *testing.T) { validators := []*types.Validator{} perPage := 100 for page := 1; ; page++ { - resp, err := client.Validators(ctx, &(h), &(page), &perPage) + resp, err := client.Voters(ctx, &(h), &(page), &perPage) require.NoError(t, err) validators = append(validators, resp.Validators...) if len(validators) == resp.Total { @@ -70,7 +70,7 @@ func TestValidator_Propose(t *testing.T) { proposeCount := 0 for _, block := range blocks { proofHash, _ := vrf.ProofToHash(block.Header.Proof.Bytes()) - proposer := types.SelectProposer(valSchedule.Set, proofHash, block.Height, block.Round) + proposer := valSchedule.Set.SelectProposer(proofHash, block.Height, block.Round) if bytes.Equal(proposer.Address, address) { expectCount++ if bytes.Equal(block.ProposerAddress, address) { diff --git a/test/maverick/consensus/state.go b/test/maverick/consensus/state.go index 412753123..96cd62c7c 100644 --- a/test/maverick/consensus/state.go +++ b/test/maverick/consensus/state.go @@ -812,7 +812,7 @@ func (cs *State) reconstructLastCommit(state sm.State) { state.LastBlockHeight)) } - lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) + lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastVoters) if !lastPrecommits.HasTwoThirdsMajority() { panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") } @@ -858,6 +858,7 @@ func (cs *State) updateToState(state sm.State) { // Reset fields based on state. validators := state.Validators + voters := state.Voters switch { case state.LastBlockHeight == 0: // Very first commit should be empty. @@ -908,9 +909,9 @@ func (cs *State) updateToState(state sm.State) { cs.ValidRound = -1 cs.ValidBlock = nil cs.ValidBlockParts = nil - cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) + cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, voters) cs.CommitRound = -1 - cs.LastValidators = state.LastValidators + cs.LastVoters = state.LastVoters cs.TriggeredTimeoutPrecommit = false cs.state = state @@ -1574,8 +1575,8 @@ func (cs *State) pruneBlocks(retainHeight int64) (uint64, error) { } func (cs *State) recordMetrics(height int64, block *types.Block) { - cs.metrics.Validators.Set(float64(cs.Validators.Size())) - cs.metrics.ValidatorsPower.Set(float64(cs.Validators.TotalVotingPower())) + cs.metrics.Voters.Set(float64(cs.Validators.Size())) + cs.metrics.VotersPower.Set(float64(cs.Validators.TotalVotingPower())) var ( missingValidators int @@ -1589,12 +1590,12 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { // after first block. var ( commitSize = block.LastCommit.Size() - valSetLen = len(cs.LastValidators.Validators) + valSetLen = len(cs.LastVoters.Voters) address types.Address ) if commitSize != valSetLen { panic(fmt.Sprintf("commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", - commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastValidators.Validators)) + commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastVoters.Voters)) } if cs.privValidator != nil { @@ -1606,7 +1607,7 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { } } - for i, val := range cs.LastValidators.Validators { + for i, val := range cs.LastVoters.Voters { commitSig := block.LastCommit.Signatures[i] if commitSig.Absent() { missingValidators++ @@ -1617,18 +1618,18 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { label := []string{ "validator_address", val.Address.String(), } - cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) + cs.metrics.VoterPower.With(label...).Set(float64(val.VotingPower)) if commitSig.ForBlock() { - cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) + cs.metrics.VoterLastSignedHeight.With(label...).Set(float64(height)) } else { - cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) + cs.metrics.VoterMissedBlocks.With(label...).Add(float64(1)) } } } } - cs.metrics.MissingValidators.Set(float64(missingValidators)) - cs.metrics.MissingValidatorsPower.Set(float64(missingValidatorsPower)) + cs.metrics.MissingVoters.Set(float64(missingValidators)) + cs.metrics.MissingVotersPower.Set(float64(missingValidatorsPower)) // NOTE: byzantine validators power and count is only for consensus evidence i.e. duplicate vote var ( @@ -1643,8 +1644,8 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { } } } - cs.metrics.ByzantineValidators.Set(float64(byzantineValidatorsCount)) - cs.metrics.ByzantineValidatorsPower.Set(float64(byzantineValidatorsPower)) + cs.metrics.ByzantineVoters.Set(float64(byzantineValidatorsCount)) + cs.metrics.ByzantineVotersPower.Set(float64(byzantineValidatorsPower)) if height > 1 { lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) diff --git a/types/block.go b/types/block.go index 74bcf5cbe..8ee78e80c 100644 --- a/types/block.go +++ b/types/block.go @@ -339,10 +339,10 @@ type Header struct { DataHash tmbytes.HexBytes `json:"data_hash"` // transactions // hashes from the app output from the prev block - ValidatorsHash tmbytes.HexBytes `json:"validators_hash"` // validators for the current block - NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` // validators for the next block - ConsensusHash tmbytes.HexBytes `json:"consensus_hash"` // consensus params for current block - AppHash tmbytes.HexBytes `json:"app_hash"` // state after txs from the previous block + VotersHash tmbytes.HexBytes `json:"voters_hash"` // voters for the current block + NextVotersHash tmbytes.HexBytes `json:"next_voters_hash"` // voters for the next block + ConsensusHash tmbytes.HexBytes `json:"consensus_hash"` // consensus params for current block + AppHash tmbytes.HexBytes `json:"app_hash"` // state after txs from the previous block // root hash of all results from the txs from the previous block LastResultsHash tmbytes.HexBytes `json:"last_results_hash"` @@ -360,7 +360,7 @@ type Header struct { func (h *Header) Populate( version tmversion.Consensus, chainID string, timestamp time.Time, lastBlockID BlockID, - valHash, nextValHash []byte, + votersHash, nextVotersHash []byte, consensusHash, appHash, lastResultsHash []byte, proposerAddress Address, round int32, @@ -370,8 +370,8 @@ func (h *Header) Populate( h.ChainID = chainID h.Time = timestamp h.LastBlockID = lastBlockID - h.ValidatorsHash = valHash - h.NextValidatorsHash = nextValHash + h.VotersHash = votersHash + h.NextVotersHash = nextVotersHash h.ConsensusHash = consensusHash h.AppHash = appHash h.LastResultsHash = lastResultsHash @@ -423,10 +423,11 @@ func (h Header) ValidateBasic() error { // Basic validation of hashes related to application data. // Will validate fully against state in state#ValidateBlock. - if err := ValidateHash(h.ValidatorsHash); err != nil { + if err := ValidateHash(h.VotersHash); err != nil { return fmt.Errorf("wrong ValidatorsHash: %v", err) } - if err := ValidateHash(h.NextValidatorsHash); err != nil { + // TODO When we add `Header.ValidatorsHash` in a future commit, we have to add a similar check here. + if err := ValidateHash(h.NextVotersHash); err != nil { return fmt.Errorf("wrong NextValidatorsHash: %v", err) } if err := ValidateHash(h.ConsensusHash); err != nil { @@ -447,7 +448,7 @@ func (h Header) ValidateBasic() error { // since a Header is not valid unless there is // a ValidatorsHash (corresponding to the validator set). func (h *Header) Hash() tmbytes.HexBytes { - if h == nil || len(h.ValidatorsHash) == 0 { + if h == nil || len(h.VotersHash) == 0 { return nil } hbz, err := h.Version.Marshal() @@ -473,8 +474,8 @@ func (h *Header) Hash() tmbytes.HexBytes { bzbi, cdcEncode(h.LastCommitHash), cdcEncode(h.DataHash), - cdcEncode(h.ValidatorsHash), - cdcEncode(h.NextValidatorsHash), + cdcEncode(h.VotersHash), + cdcEncode(h.NextVotersHash), cdcEncode(h.ConsensusHash), cdcEncode(h.AppHash), cdcEncode(h.LastResultsHash), @@ -516,8 +517,8 @@ func (h *Header) StringIndented(indent string) string { indent, h.LastBlockID, indent, h.LastCommitHash, indent, h.DataHash, - indent, h.ValidatorsHash, - indent, h.NextValidatorsHash, + indent, h.VotersHash, + indent, h.NextVotersHash, indent, h.AppHash, indent, h.ConsensusHash, indent, h.LastResultsHash, @@ -535,22 +536,22 @@ func (h *Header) ToProto() *tmproto.Header { } return &tmproto.Header{ - Version: h.Version, - ChainID: h.ChainID, - Height: h.Height, - Time: h.Time, - LastBlockId: h.LastBlockID.ToProto(), - ValidatorsHash: h.ValidatorsHash, - NextValidatorsHash: h.NextValidatorsHash, - ConsensusHash: h.ConsensusHash, - AppHash: h.AppHash, - DataHash: h.DataHash, - EvidenceHash: h.EvidenceHash, - LastResultsHash: h.LastResultsHash, - LastCommitHash: h.LastCommitHash, - ProposerAddress: h.ProposerAddress, - Round: h.Round, - Proof: h.Proof, + Version: h.Version, + ChainID: h.ChainID, + Height: h.Height, + Time: h.Time, + LastBlockId: h.LastBlockID.ToProto(), + VotersHash: h.VotersHash, + NextVotersHash: h.NextVotersHash, + ConsensusHash: h.ConsensusHash, + AppHash: h.AppHash, + DataHash: h.DataHash, + EvidenceHash: h.EvidenceHash, + LastResultsHash: h.LastResultsHash, + LastCommitHash: h.LastCommitHash, + ProposerAddress: h.ProposerAddress, + Round: h.Round, + Proof: h.Proof, } } @@ -574,8 +575,8 @@ func HeaderFromProto(ph *tmproto.Header) (Header, error) { h.Time = ph.Time h.Height = ph.Height h.LastBlockID = *bi - h.ValidatorsHash = ph.ValidatorsHash - h.NextValidatorsHash = ph.NextValidatorsHash + h.VotersHash = ph.VotersHash + h.NextVotersHash = ph.NextVotersHash h.ConsensusHash = ph.ConsensusHash h.AppHash = ph.AppHash h.DataHash = ph.DataHash @@ -756,9 +757,9 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error { // NOTE: Commit is empty for height 1, but never nil. type Commit struct { // NOTE: The signatures are in order of address to preserve the bonded - // ValidatorSet order. + // VoterSet order. // Any peer with a block can gossip signatures by index with a peer without - // recalculating the active ValidatorSet. + // recalculating the active VoterSet. Height int64 `json:"height"` Round int32 `json:"round"` BlockID BlockID `json:"block_id"` @@ -784,8 +785,8 @@ func NewCommit(height int64, round int32, blockID BlockID, commitSigs []CommitSi // CommitToVoteSet constructs a VoteSet from the Commit and validator set. // Panics if signatures from the commit can't be added to the voteset. // Inverse of VoteSet.MakeCommit(). -func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSet { - voteSet := NewVoteSet(chainID, commit.Height, commit.Round, tmproto.PrecommitType, vals) +func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet { + voteSet := NewVoteSet(chainID, commit.Height, commit.Round, tmproto.PrecommitType, voters) for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some precommits can be missing. diff --git a/types/block_test.go b/types/block_test.go index 007ce8cf7..75160909f 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -39,7 +39,7 @@ func TestBlockAddEvidence(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) @@ -59,7 +59,7 @@ func TestBlockValidateBasic(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, valSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) @@ -73,7 +73,7 @@ func TestBlockValidateBasic(t *testing.T) { }{ {"Make Block", func(blk *Block) {}, false}, {"Make Block w/ proposer Addr", func(blk *Block) { - blk.ProposerAddress = SelectProposer(valSet, []byte{}, blk.Height, 0).Address + blk.ProposerAddress = valSet.SelectProposer([]byte{}, blk.Height, 0).Address }, false}, {"Negative Height", func(blk *Block) { blk.Height = -1 }, true}, {"Remove 1/2 the commits", func(blk *Block) { @@ -100,7 +100,7 @@ func TestBlockValidateBasic(t *testing.T) { i := i t.Run(tc.testName, func(t *testing.T) { block := MakeBlock(h, txs, commit, evList) - block.ProposerAddress = SelectProposer(valSet, []byte{}, block.Height, 0).Address + block.ProposerAddress = valSet.SelectProposer([]byte{}, block.Height, 0).Address tc.malleateBlock(block) err = block.ValidateBasic() assert.Equal(t, tc.expErr, err != nil, "#%d: %v", i, err) @@ -127,7 +127,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) @@ -144,7 +144,7 @@ func TestBlockHashesTo(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, voterSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) @@ -152,7 +152,7 @@ func TestBlockHashesTo(t *testing.T) { evList := []Evidence{ev} block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList) - block.ValidatorsHash = valSet.Hash() + block.VotersHash = voterSet.Hash() assert.False(t, block.HashesTo([]byte{})) assert.False(t, block.HashesTo([]byte("something else"))) assert.True(t, block.HashesTo(block.Hash())) @@ -222,7 +222,7 @@ func TestNilDataHashDoesntCrash(t *testing.T) { func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) @@ -313,41 +313,41 @@ func TestHeaderHash(t *testing.T) { expectHash bytes.HexBytes }{ {"Generates expected hash", &Header{ - Version: tmversion.Consensus{Block: 1, App: 2}, - ChainID: "chainId", - Height: 3, - Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC), - LastBlockID: makeBlockID(make([]byte, tmhash.Size), 6, make([]byte, tmhash.Size)), - LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), - DataHash: tmhash.Sum([]byte("data_hash")), - ValidatorsHash: tmhash.Sum([]byte("validators_hash")), - NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")), - ConsensusHash: tmhash.Sum([]byte("consensus_hash")), - AppHash: tmhash.Sum([]byte("app_hash")), - LastResultsHash: tmhash.Sum([]byte("last_results_hash")), - EvidenceHash: tmhash.Sum([]byte("evidence_hash")), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), - Round: 1, - Proof: tmhash.Sum([]byte("proof")), - }, hexBytesFromString("2F6751BE4744207CFB98C7315923CFFA894C6196E0ED9475EE7F4A44D0CBA713")}, + Version: tmversion.Consensus{Block: 1, App: 2}, + ChainID: "chainId", + Height: 3, + Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC), + LastBlockID: makeBlockID(make([]byte, tmhash.Size), 6, make([]byte, tmhash.Size)), + LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), + DataHash: tmhash.Sum([]byte("data_hash")), + VotersHash: tmhash.Sum([]byte("voters_hash")), + NextVotersHash: tmhash.Sum([]byte("next_voters_hash")), + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: tmhash.Sum([]byte("app_hash")), + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + Round: 1, + Proof: tmhash.Sum([]byte("proof")), + }, hexBytesFromString("0ECEA9AA5613ECD1673C223FA92A4651727C3DD7AF61E2C5FA979EEDBCC05F37")}, {"nil header yields nil", nil, nil}, - {"nil ValidatorsHash yields nil", &Header{ - Version: tmversion.Consensus{Block: 1, App: 2}, - ChainID: "chainId", - Height: 3, - Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC), - LastBlockID: makeBlockID(make([]byte, tmhash.Size), 6, make([]byte, tmhash.Size)), - LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), - DataHash: tmhash.Sum([]byte("data_hash")), - ValidatorsHash: nil, - NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")), - ConsensusHash: tmhash.Sum([]byte("consensus_hash")), - AppHash: tmhash.Sum([]byte("app_hash")), - LastResultsHash: tmhash.Sum([]byte("last_results_hash")), - EvidenceHash: tmhash.Sum([]byte("evidence_hash")), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), - Round: 1, - Proof: tmhash.Sum([]byte("proof")), + {"nil VotersHash yields nil", &Header{ + Version: tmversion.Consensus{Block: 1, App: 2}, + ChainID: "chainId", + Height: 3, + Time: time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC), + LastBlockID: makeBlockID(make([]byte, tmhash.Size), 6, make([]byte, tmhash.Size)), + LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), + DataHash: tmhash.Sum([]byte("data_hash")), + VotersHash: nil, + NextVotersHash: tmhash.Sum([]byte("next_voters_hash")), + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: tmhash.Sum([]byte("app_hash")), + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + Round: 1, + Proof: tmhash.Sum([]byte("proof")), }, nil}, } for _, tc := range testCases { @@ -409,20 +409,20 @@ func TestMaxHeaderBytes(t *testing.T) { timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) h := Header{ - Version: tmversion.Consensus{Block: math.MaxInt64, App: math.MaxInt64}, - ChainID: maxChainID, - Height: math.MaxInt64, - Time: timestamp, - LastBlockID: makeBlockID(make([]byte, tmhash.Size), math.MaxInt32, make([]byte, tmhash.Size)), - LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), - DataHash: tmhash.Sum([]byte("data_hash")), - ValidatorsHash: tmhash.Sum([]byte("validators_hash")), - NextValidatorsHash: tmhash.Sum([]byte("next_validators_hash")), - ConsensusHash: tmhash.Sum([]byte("consensus_hash")), - AppHash: tmhash.Sum([]byte("app_hash")), - LastResultsHash: tmhash.Sum([]byte("last_results_hash")), - EvidenceHash: tmhash.Sum([]byte("evidence_hash")), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + Version: tmversion.Consensus{Block: math.MaxInt64, App: math.MaxInt64}, + ChainID: maxChainID, + Height: math.MaxInt64, + Time: timestamp, + LastBlockID: makeBlockID(make([]byte, tmhash.Size), math.MaxInt32, make([]byte, tmhash.Size)), + LastCommitHash: tmhash.Sum([]byte("last_commit_hash")), + DataHash: tmhash.Sum([]byte("data_hash")), + VotersHash: tmhash.Sum([]byte("voters_hash")), + NextVotersHash: tmhash.Sum([]byte("next_voters_hash")), + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: tmhash.Sum([]byte("app_hash")), + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), } bz, err := h.ToProto().Marshal() @@ -434,7 +434,7 @@ func TestMaxHeaderBytes(t *testing.T) { func randCommit(now time.Time) *Commit { lastID := makeBlockIDRandom() h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, now) if err != nil { panic(err) @@ -515,7 +515,7 @@ func TestCommitToVoteSet(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) assert.NoError(t, err) @@ -558,7 +558,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } for _, tc := range testCases { - voteSet, valSet, vals := randVoteSet(height-1, round, tmproto.PrecommitType, tc.numValidators, 1) + voteSet, _, valSet, vals := randVoteSet(height-1, round, tmproto.PrecommitType, tc.numValidators, 1) vi := int32(0) for n := range tc.blockIDs { @@ -747,17 +747,17 @@ func makeRandHeader() Header { randBytes := tmrand.Bytes(tmhash.Size) randAddress := tmrand.Bytes(crypto.AddressSize) h := Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, - ChainID: chainID, - Height: height, - Time: t, - LastBlockID: BlockID{}, - LastCommitHash: randBytes, - DataHash: randBytes, - ValidatorsHash: randBytes, - NextValidatorsHash: randBytes, - ConsensusHash: randBytes, - AppHash: randBytes, + Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, + ChainID: chainID, + Height: height, + Time: t, + LastBlockID: BlockID{}, + LastCommitHash: randBytes, + DataHash: randBytes, + VotersHash: randBytes, + NextVotersHash: randBytes, + ConsensusHash: randBytes, + AppHash: randBytes, LastResultsHash: randBytes, diff --git a/types/evidence.go b/types/evidence.go index 8007763b7..1a69f522a 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -46,12 +46,12 @@ var _ Evidence = &DuplicateVoteEvidence{} // NewDuplicateVoteEvidence creates DuplicateVoteEvidence with right ordering given // two conflicting votes. If one of the votes is nil, evidence returned is nil as well -func NewDuplicateVoteEvidence(vote1, vote2 *Vote, blockTime time.Time, valSet *ValidatorSet) *DuplicateVoteEvidence { +func NewDuplicateVoteEvidence(vote1, vote2 *Vote, blockTime time.Time, voterSet *VoterSet) *DuplicateVoteEvidence { var voteA, voteB *Vote - if vote1 == nil || vote2 == nil || valSet == nil { + if vote1 == nil || vote2 == nil || voterSet == nil { return nil } - idx, val := valSet.GetByAddress(vote1.ValidatorAddress) + idx, val := voterSet.GetByAddress(vote1.ValidatorAddress) if idx == -1 { return nil } @@ -66,7 +66,7 @@ func NewDuplicateVoteEvidence(vote1, vote2 *Vote, blockTime time.Time, valSet *V return &DuplicateVoteEvidence{ VoteA: voteA, VoteB: voteB, - TotalVotingPower: valSet.TotalVotingPower(), + TotalVotingPower: voterSet.TotalVotingPower(), ValidatorPower: val.VotingPower, Timestamp: blockTime, } @@ -230,7 +230,7 @@ func (l *LightClientAttackEvidence) Bytes() []byte { // GetByzantineValidators finds out what style of attack LightClientAttackEvidence was and then works out who // the malicious validators were and returns them. This is used both for forming the ByzantineValidators // field and for validating that it is correct. Validators are ordered based on validator power -func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *ValidatorSet, +func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *VoterSet, trusted *SignedHeader) []*Validator { var validators []*Validator // First check if the header is invalid. This means that it is a lunatic attack and therefore we take the @@ -266,7 +266,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator continue } - _, val := l.ConflictingBlock.ValidatorSet.GetByAddress(sigA.ValidatorAddress) + _, val := l.ConflictingBlock.VoterSet.GetByAddress(sigA.ValidatorAddress) validators = append(validators, val) } sort.Sort(ValidatorsByVotingPower(validators)) @@ -283,8 +283,8 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator // or not. If it is then all the deterministic fields of the header should be the same. // If not, it is an invalid header and constitutes a lunatic attack. func (l *LightClientAttackEvidence) ConflictingHeaderIsInvalid(trustedHeader *Header) bool { - return !bytes.Equal(trustedHeader.ValidatorsHash, l.ConflictingBlock.ValidatorsHash) || - !bytes.Equal(trustedHeader.NextValidatorsHash, l.ConflictingBlock.NextValidatorsHash) || + return !bytes.Equal(trustedHeader.VotersHash, l.ConflictingBlock.VotersHash) || + !bytes.Equal(trustedHeader.NextVotersHash, l.ConflictingBlock.NextVotersHash) || !bytes.Equal(trustedHeader.ConsensusHash, l.ConflictingBlock.ConsensusHash) || !bytes.Equal(trustedHeader.AppHash, l.ConflictingBlock.AppHash) || !bytes.Equal(trustedHeader.LastResultsHash, l.ConflictingBlock.LastResultsHash) @@ -557,7 +557,7 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time, vB := voteB.ToProto() _ = pv.SignVote(chainID, vB) voteB.Signature = vB.Signature - return NewDuplicateVoteEvidence(voteA, voteB, time, NewValidatorSet([]*Validator{val})) + return NewDuplicateVoteEvidence(voteA, voteB, time, NewVoterSet([]*Validator{val})) } func makeMockVote(height int64, round, index int32, addr Address, diff --git a/types/evidence_test.go b/types/evidence_test.go index 2e61f6a9c..ed01ede0a 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -81,7 +81,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime) vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime) - valSet := NewValidatorSet([]*Validator{val.ExtractIntoValidator(10)}) + valSet := NewVoterSet([]*Validator{val.ExtractIntoValidator(10)}) ev := NewDuplicateVoteEvidence(vote1, vote2, defaultVoteTime, valSet) tc.malleateEvidence(ev) assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") @@ -91,7 +91,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { func TestLightClientAttackEvidence(t *testing.T) { height := int64(5) - voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) + voteSet, _, voterSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) header := makeHeaderRandom() header.Height = height blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) @@ -103,7 +103,7 @@ func TestLightClientAttackEvidence(t *testing.T) { Header: header, Commit: commit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 1, } @@ -118,7 +118,7 @@ func TestLightClientAttackEvidence(t *testing.T) { Header: header, Commit: differentCommit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 1, } @@ -131,7 +131,7 @@ func TestLightClientAttackEvidence(t *testing.T) { Header: differentHeader, Commit: differentCommit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 1, } @@ -143,7 +143,7 @@ func TestLightClientAttackEvidence(t *testing.T) { Header: header, Commit: differentCommit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 2, } @@ -154,10 +154,10 @@ func TestLightClientAttackEvidence(t *testing.T) { func TestLightClientAttackEvidenceValidation(t *testing.T) { height := int64(5) - voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) + voteSet, valSet, voterSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, 10, 1) header := makeHeaderRandom() header.Height = height - header.ValidatorsHash = valSet.Hash() + header.VotersHash = valSet.Hash() blockID := makeBlockID(header.Hash(), math.MaxInt32, tmhash.Sum([]byte("partshash"))) commit, err := MakeCommit(blockID, height, 1, voteSet, privVals, time.Now()) require.NoError(t, err) @@ -167,7 +167,7 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { Header: header, Commit: commit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 1, } @@ -186,7 +186,7 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { {"Nil conflicting header", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock.Header = nil }, true}, {"Nil conflicting blocl", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock = nil }, true}, {"Nil validator set", func(ev *LightClientAttackEvidence) { - ev.ConflictingBlock.ValidatorSet = &ValidatorSet{} + ev.ConflictingBlock.VoterSet = &VoterSet{} }, true}, } for _, tc := range testCases { @@ -198,7 +198,7 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) { Header: header, Commit: commit, }, - ValidatorSet: valSet, + VoterSet: voterSet, }, CommonHeight: height - 1, } @@ -244,20 +244,20 @@ func makeVote( func makeHeaderRandom() *Header { return &Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, - ChainID: tmrand.Str(12), - Height: int64(tmrand.Uint16()) + 1, - Time: time.Now(), - LastBlockID: makeBlockIDRandom(), - LastCommitHash: crypto.CRandBytes(tmhash.Size), - DataHash: crypto.CRandBytes(tmhash.Size), - ValidatorsHash: crypto.CRandBytes(tmhash.Size), - NextValidatorsHash: crypto.CRandBytes(tmhash.Size), - ConsensusHash: crypto.CRandBytes(tmhash.Size), - AppHash: crypto.CRandBytes(tmhash.Size), - LastResultsHash: crypto.CRandBytes(tmhash.Size), - EvidenceHash: crypto.CRandBytes(tmhash.Size), - ProposerAddress: crypto.CRandBytes(crypto.AddressSize), + Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, + ChainID: tmrand.Str(12), + Height: int64(tmrand.Uint16()) + 1, + Time: time.Now(), + LastBlockID: makeBlockIDRandom(), + LastCommitHash: crypto.CRandBytes(tmhash.Size), + DataHash: crypto.CRandBytes(tmhash.Size), + VotersHash: crypto.CRandBytes(tmhash.Size), + NextVotersHash: crypto.CRandBytes(tmhash.Size), + ConsensusHash: crypto.CRandBytes(tmhash.Size), + AppHash: crypto.CRandBytes(tmhash.Size), + LastResultsHash: crypto.CRandBytes(tmhash.Size), + EvidenceHash: crypto.CRandBytes(tmhash.Size), + ProposerAddress: crypto.CRandBytes(crypto.AddressSize), } } diff --git a/types/light.go b/types/light.go index 8f09d8205..8ddd5f320 100644 --- a/types/light.go +++ b/types/light.go @@ -13,6 +13,7 @@ import ( type LightBlock struct { *SignedHeader `json:"signed_header"` ValidatorSet *ValidatorSet `json:"validator_set"` + VoterSet *VoterSet `json:"voter_set"` } // ValidateBasic checks that the data is correct and consistent @@ -22,21 +23,21 @@ func (lb LightBlock) ValidateBasic(chainID string) error { if lb.SignedHeader == nil { return errors.New("missing signed header") } - if lb.ValidatorSet == nil { + if lb.VoterSet == nil { return errors.New("missing validator set") } if err := lb.SignedHeader.ValidateBasic(chainID); err != nil { return fmt.Errorf("invalid signed header: %w", err) } - if err := lb.ValidatorSet.ValidateBasic(); err != nil { + if err := lb.VoterSet.ValidateBasic(); err != nil { return fmt.Errorf("invalid validator set: %w", err) } // make sure the validator set is consistent with the header - if valSetHash := lb.ValidatorSet.Hash(); !bytes.Equal(lb.SignedHeader.ValidatorsHash, valSetHash) { - return fmt.Errorf("expected validator hash of header to match validator set hash (%X != %X)", - lb.SignedHeader.ValidatorsHash, valSetHash, + if voterSetHash := lb.VoterSet.Hash(); !bytes.Equal(lb.SignedHeader.VotersHash, voterSetHash) { + return fmt.Errorf("expected voter hash of header to match validator set hash (%X != %X)", + lb.SignedHeader.VotersHash, voterSetHash, ) } @@ -58,7 +59,7 @@ func (lb LightBlock) StringIndented(indent string) string { %s %v %s}`, indent, lb.SignedHeader.StringIndented(indent+" "), - indent, lb.ValidatorSet.StringIndented(indent+" "), + indent, lb.VoterSet.StringIndented(indent+" "), indent) } @@ -73,8 +74,8 @@ func (lb *LightBlock) ToProto() (*tmproto.LightBlock, error) { if lb.SignedHeader != nil { lbp.SignedHeader = lb.SignedHeader.ToProto() } - if lb.ValidatorSet != nil { - lbp.ValidatorSet, err = lb.ValidatorSet.ToProto() + if lb.VoterSet != nil { + lbp.VoterSet, err = lb.VoterSet.ToProto() if err != nil { return nil, err } @@ -100,12 +101,12 @@ func LightBlockFromProto(pb *tmproto.LightBlock) (*LightBlock, error) { lb.SignedHeader = sh } - if pb.ValidatorSet != nil { - vals, err := ValidatorSetFromProto(pb.ValidatorSet) + if pb.VoterSet != nil { + vals, err := VoterSetFromProto(pb.VoterSet) if err != nil { return nil, err } - lb.ValidatorSet = vals + lb.VoterSet = vals } return lb, nil diff --git a/types/light_test.go b/types/light_test.go index 04a4bbe7e..f81fd1870 100644 --- a/types/light_test.go +++ b/types/light_test.go @@ -15,14 +15,14 @@ import ( func TestLightBlockValidateBasic(t *testing.T) { header := makeRandHeader() commit := randCommit(time.Now()) - vals, _ := RandValidatorSet(5, 1) + _, voters, _ := RandVoterSet(5, 1) header.Height = commit.Height header.LastBlockID = commit.BlockID - header.ValidatorsHash = vals.Hash() + header.VotersHash = voters.Hash() header.Version.Block = version.BlockProtocol - vals2, _ := RandValidatorSet(3, 1) - vals3 := vals.Copy() - vals3.Validators[4] = &Validator{} + _, voters2, _ := RandVoterSet(3, 1) + voters3 := voters2.Copy() + voters3.Voters[4] = &Validator{} commit.BlockID.Hash = header.Hash() sh := &SignedHeader{ @@ -33,19 +33,19 @@ func TestLightBlockValidateBasic(t *testing.T) { testCases := []struct { name string sh *SignedHeader - vals *ValidatorSet + vals *VoterSet expectErr bool }{ - {"valid light block", sh, vals, false}, - {"hashes don't match", sh, vals2, true}, - {"invalid validator set", sh, vals3, true}, - {"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit(time.Now())}, vals, true}, + {"valid light block", sh, voters, false}, + {"hashes don't match", sh, voters2, true}, + {"invalid validator set", sh, voters3, true}, + {"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit(time.Now())}, voters, true}, } for _, tc := range testCases { lightBlock := LightBlock{ SignedHeader: tc.sh, - ValidatorSet: tc.vals, + VoterSet: tc.vals, } err := lightBlock.ValidateBasic(header.ChainID) if tc.expectErr { @@ -60,11 +60,11 @@ func TestLightBlockValidateBasic(t *testing.T) { func TestLightBlockProtobuf(t *testing.T) { header := makeRandHeader() commit := randCommit(time.Now()) - vals, _ := RandValidatorSet(5, 1) + _, voters, _ := RandVoterSet(5, 1) header.Height = commit.Height header.LastBlockID = commit.BlockID header.Version.Block = version.BlockProtocol - header.ValidatorsHash = vals.Hash() + header.VotersHash = voters.Hash() commit.BlockID.Hash = header.Hash() sh := &SignedHeader{ @@ -75,20 +75,20 @@ func TestLightBlockProtobuf(t *testing.T) { testCases := []struct { name string sh *SignedHeader - vals *ValidatorSet + vals *VoterSet toProtoErr bool toBlockErr bool }{ - {"valid light block", sh, vals, false, false}, - {"empty signed header", &SignedHeader{}, vals, false, false}, - {"empty validator set", sh, &ValidatorSet{}, false, true}, - {"empty light block", &SignedHeader{}, &ValidatorSet{}, false, true}, + {"valid light block", sh, voters, false, false}, + {"empty signed header", &SignedHeader{}, voters, false, false}, + {"empty validator set", sh, &VoterSet{}, false, true}, + {"empty light block", &SignedHeader{}, &VoterSet{}, false, true}, } for _, tc := range testCases { lightBlock := &LightBlock{ SignedHeader: tc.sh, - ValidatorSet: tc.vals, + VoterSet: tc.vals, } lbp, err := lightBlock.ToProto() if tc.toProtoErr { @@ -113,20 +113,20 @@ func TestSignedHeaderValidateBasic(t *testing.T) { chainID := "𠜎" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) h := Header{ - Version: tmversion.Consensus{Block: version.BlockProtocol, App: math.MaxInt64}, - ChainID: chainID, - Height: commit.Height, - Time: timestamp, - LastBlockID: commit.BlockID, - LastCommitHash: commit.Hash(), - DataHash: commit.Hash(), - ValidatorsHash: commit.Hash(), - NextValidatorsHash: commit.Hash(), - ConsensusHash: commit.Hash(), - AppHash: commit.Hash(), - LastResultsHash: commit.Hash(), - EvidenceHash: commit.Hash(), - ProposerAddress: crypto.AddressHash([]byte("proposer_address")), + Version: tmversion.Consensus{Block: version.BlockProtocol, App: math.MaxInt64}, + ChainID: chainID, + Height: commit.Height, + Time: timestamp, + LastBlockID: commit.BlockID, + LastCommitHash: commit.Hash(), + DataHash: commit.Hash(), + VotersHash: commit.Hash(), + NextVotersHash: commit.Hash(), + ConsensusHash: commit.Hash(), + AppHash: commit.Hash(), + LastResultsHash: commit.Hash(), + EvidenceHash: commit.Hash(), + ProposerAddress: crypto.AddressHash([]byte("proposer_address")), } validSignedHeader := SignedHeader{Header: &h, Commit: commit} diff --git a/types/protobuf.go b/types/protobuf.go index 1ee094a9f..320ebdbc6 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -44,11 +44,11 @@ func (tm2pb) Header(header *Header) tmproto.Header { LastCommitHash: header.LastCommitHash, DataHash: header.DataHash, - ValidatorsHash: header.ValidatorsHash, - NextValidatorsHash: header.NextValidatorsHash, - ConsensusHash: header.ConsensusHash, - AppHash: header.AppHash, - LastResultsHash: header.LastResultsHash, + VotersHash: header.VotersHash, + NextVotersHash: header.NextVotersHash, + ConsensusHash: header.ConsensusHash, + AppHash: header.AppHash, + LastResultsHash: header.LastResultsHash, EvidenceHash: header.EvidenceHash, ProposerAddress: header.ProposerAddress, diff --git a/types/validator_set.go b/types/validator_set.go index 71ea7655a..8c5426151 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "encoding/binary" "errors" "fmt" "math" @@ -11,8 +10,6 @@ import ( "strings" "github.com/tendermint/tendermint/crypto/merkle" - "github.com/tendermint/tendermint/crypto/tmhash" - tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -59,8 +56,8 @@ type ValidatorSet struct { totalVotingPower int64 } -// NewValidatorSet initializes a ValidatorSet by copying over the values from -// `valz`, a list of Validators. If valz is nil or empty, the new ValidatorSet +// NewValidatorSet initializes a VoterSet by copying over the values from +// `valz`, a list of Validators. If valz is nil or empty, the new VoterSet // will have an empty list of Validators. // // The addresses of validators in `valz` must be unique otherwise the function @@ -238,7 +235,7 @@ func validatorListCopy(valsList []*Validator) []*Validator { return valsCopy } -// Copy each validator into a new ValidatorSet. +// Copy each validator into a new VoterSet. func (vals *ValidatorSet) Copy() *ValidatorSet { return &ValidatorSet{ Validators: validatorListCopy(vals.Validators), @@ -622,209 +619,18 @@ func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { return vals.updateWithChangeSet(changes, true) } -// VerifyCommit verifies +2/3 of the set had signed the given commit. -// -// It checks all the signatures! While it's safe to exit as soon as we have -// 2/3+ signatures, doing so would impact incentivization logic in the ABCI -// application that depends on the LastCommitInfo sent in BeginBlock, which -// includes which validators signed. For instance, Gaia incentivizes proposers -// with a bonus for including more than +2/3 of the signatures. -func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, - height int64, commit *Commit) error { - - if vals.Size() != len(commit.Signatures) { - return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures)) - } - - // Validate Height and BlockID. - if height != commit.Height { - return NewErrInvalidCommitHeight(height, commit.Height) - } - if !blockID.Equals(commit.BlockID) { - return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", - blockID, commit.BlockID) - } - - talliedVotingPower := int64(0) - votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 - for idx, commitSig := range commit.Signatures { - if commitSig.Absent() { - continue // OK, some signatures can be absent. - } - - // The vals and commit have a 1-to-1 correspondance. - // This means we don't need the validator address or to do any lookup. - val := vals.Validators[idx] - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - // Good! - if commitSig.ForBlock() { - talliedVotingPower += val.VotingPower - } - // else { - // It's OK. We include stray signatures (~votes for nil) to measure - // validator availability. - // } - } - - if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} - } - - return nil -} - -// LIGHT CLIENT VERIFICATION METHODS - -// VerifyCommitLight verifies +2/3 of the set had signed the given commit. -// -// This method is primarily used by the light client and does not check all the -// signatures. -func (vals *ValidatorSet) VerifyCommitLight(chainID string, blockID BlockID, - height int64, commit *Commit) error { - - if vals.Size() != len(commit.Signatures) { - return NewErrInvalidCommitSignatures(vals.Size(), len(commit.Signatures)) - } - - // Validate Height and BlockID. - if height != commit.Height { - return NewErrInvalidCommitHeight(height, commit.Height) - } - if !blockID.Equals(commit.BlockID) { - return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", - blockID, commit.BlockID) - } - - talliedVotingPower := int64(0) - votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 - for idx, commitSig := range commit.Signatures { - // No need to verify absent or nil votes. - if !commitSig.ForBlock() { - continue - } - - // The vals and commit have a 1-to-1 correspondance. - // This means we don't need the validator address or to do any lookup. - val := vals.Validators[idx] - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - - talliedVotingPower += val.VotingPower - - // return as soon as +2/3 of the signatures are verified - if talliedVotingPower > votingPowerNeeded { - return nil - } - } - - return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} -} - -// VerifyCommitLightTrusting verifies that trustLevel of the validator set signed -// this commit. -// -// NOTE the given validators do not necessarily correspond to the validator set -// for this commit, but there may be some intersection. -// -// This method is primarily used by the light client and does not check all the -// signatures. -func (vals *ValidatorSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel tmmath.Fraction) error { - // sanity check - if trustLevel.Denominator == 0 { - return errors.New("trustLevel has zero Denominator") - } - - var ( - talliedVotingPower int64 - seenVals = make(map[int32]int, len(commit.Signatures)) // validator index -> commit index - ) - - // Safely calculate voting power needed. - totalVotingPowerMulByNumerator, overflow := safeMul(vals.TotalVotingPower(), int64(trustLevel.Numerator)) - if overflow { - return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator") - } - votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) - - for idx, commitSig := range commit.Signatures { - // No need to verify absent or nil votes. - if !commitSig.ForBlock() { - continue - } - - // We don't know the validators that committed this block, so we have to - // check for each vote if its validator is already known. - valIdx, val := vals.GetByAddress(commitSig.ValidatorAddress) - - if val != nil { - // check for double vote of validator on the same commit - if firstIndex, ok := seenVals[valIdx]; ok { - secondIndex := idx - return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) - } - seenVals[valIdx] = idx - - // Validate signature. - voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) - if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { - return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) - } - - talliedVotingPower += val.VotingPower - - if talliedVotingPower > votingPowerNeeded { - return nil - } - } +func (vals *ValidatorSet) SelectProposer(proofHash []byte, height int64, round int32) *Validator { + if vals.IsNilOrEmpty() { + panic("empty validator set") } - - return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} -} - -// findPreviousProposer reverses the compare proposer priority function to find the validator -// with the lowest proposer priority which would have been the previous proposer. -// -// Is used when recreating a validator set from an existing array of validators. -func (vals *ValidatorSet) findPreviousProposer() *Validator { - var previousProposer *Validator - for _, val := range vals.Validators { - if previousProposer == nil { - previousProposer = val - continue - } - if previousProposer == previousProposer.CompareProposerPriority(val) { - previousProposer = val - } + seed := hashToSeed(MakeRoundHash(proofHash, height, round)) + candidates := make([]tmrand.Candidate, len(vals.Validators)) + for i, val := range vals.Validators { + candidates[i] = &candidate{idx: i, val: val} } - return previousProposer -} - -//----------------- - -// IsErrNotEnoughVotingPowerSigned returns true if err is -// ErrNotEnoughVotingPowerSigned. -func IsErrNotEnoughVotingPowerSigned(err error) bool { - return errors.As(err, &ErrNotEnoughVotingPowerSigned{}) -} - -// ErrNotEnoughVotingPowerSigned is returned when not enough validators signed -// a commit. -type ErrNotEnoughVotingPowerSigned struct { - Got int64 - Needed int64 -} - -func (e ErrNotEnoughVotingPowerSigned) Error() string { - return fmt.Sprintf("invalid commit -- insufficient voting power: got %d, needed more than %d", e.Got, e.Needed) + samples := tmrand.RandomSamplingWithPriority(seed, candidates, 1, uint64(vals.TotalVotingPower())) + proposerIdx := samples[0].(*candidate).idx + return vals.Validators[proposerIdx] } //---------------- @@ -841,7 +647,7 @@ func (vals *ValidatorSet) String() string { // See Validator#String. func (vals *ValidatorSet) StringIndented(indent string) string { if vals == nil { - return "nil-ValidatorSet" + return "nil-VoterSet" } var valStrings []string vals.Iterate(func(index int, val *Validator) bool { @@ -913,8 +719,7 @@ func (vals *ValidatorSet) ToProto() (*tmproto.ValidatorSet, error) { } // ValidatorSetFromProto sets a protobuf ValidatorSet to the given pointer. -// It returns an error if any of the validators from the set or the proposer -// is invalid +// It returns an error if any of the validators from the set is invalid func ValidatorSetFromProto(vp *tmproto.ValidatorSet) (*ValidatorSet, error) { if vp == nil { return nil, errors.New("nil validator set") // validator set should never be nil, bigger issues are at play if empty @@ -961,29 +766,8 @@ func ValidatorSetFromExistingValidators(valz []*Validator) (*ValidatorSet, error //---------------------------------------- -// RandValidatorSet returns a randomized validator set (size: +numValidators+), -// where each validator has a voting power of +votingPower+. -// -// EXPOSED FOR TESTING. -func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []PrivValidator) { - var ( - valz = make([]*Validator, numValidators) - privValidators = make([]PrivValidator, numValidators) - ) - - for i := 0; i < numValidators; i++ { - val, privValidator := RandValidator(false, votingPower) - valz[i] = val - privValidators[i] = privValidator - } - - vals := NewValidatorSet(valz) - sort.Sort(PrivValidatorsByAddress(privValidators)) - - return vals, privValidators -} - -// safe addition/subtraction/multiplication +/////////////////////////////////////////////////////////////////////////////// +// safe addition/subtraction func safeAdd(a, b int64) (int64, bool) { if b > 0 && a > math.MaxInt64-b { @@ -1046,60 +830,3 @@ func safeMul(a, b int64) (int64, bool) { return a * b, false } - -// candidate save simple validator data for selecting proposer -type candidate struct { - idx int - address Address - votingPower int64 -} - -func (c *candidate) Priority() uint64 { - // TODO Is it possible to have a negative VotingPower? - if c.votingPower < 0 { - return 0 - } - return uint64(c.votingPower) -} - -func (c *candidate) LessThan(other tmrand.Candidate) bool { - o, ok := other.(*candidate) - if !ok { - panic("incompatible type") - } - return bytes.Compare(c.address, o.address) < 0 -} - -func SelectProposer(validators *ValidatorSet, proofHash []byte, height int64, round int32) *Validator { - if validators.IsNilOrEmpty() { - panic("empty validator set") - } - seed := hashToSeed(MakeRoundHash(proofHash, height, round)) - candidates := make([]tmrand.Candidate, len(validators.Validators)) - for i, val := range validators.Validators { - candidates[i] = &candidate{idx: i, address: val.Address, votingPower: val.VotingPower} - } - vals := tmrand.RandomSamplingWithPriority(seed, candidates, 1, uint64(validators.TotalVotingPower())) - proposerIdx := vals[0].(*candidate).idx - return validators.Validators[proposerIdx] -} - -func hashToSeed(hash []byte) uint64 { - for len(hash) < 8 { - hash = append(hash, byte(0)) - } - return binary.LittleEndian.Uint64(hash[:8]) -} - -// MakeRoundHash combines the VRF hash, block height, and round to create a hash value for each round. This value is -// used for random sampling of the Proposer. -func MakeRoundHash(proofHash []byte, height int64, round int32) []byte { - b := make([]byte, 16) - binary.LittleEndian.PutUint64(b, uint64(height)) - binary.LittleEndian.PutUint64(b[8:], uint64(round)) - hash := tmhash.New() - hash.Write(proofHash) - hash.Write(b[:8]) - hash.Write(b[8:16]) - return hash.Sum(nil) -} diff --git a/types/validator_set_test.go b/types/validator_set_test.go index d6429102f..ea6d78e19 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -27,11 +27,11 @@ func TestValidatorSetBasic(t *testing.T) { // but attempting to IncrementProposerPriority on them will panic. vset := NewValidatorSet([]*Validator{}) assert.Panics(t, func() { vset.IncrementProposerPriority(1) }) - assert.Panics(t, func() { SelectProposer(vset, []byte{}, 1, 0) }) + assert.Panics(t, func() { vset.SelectProposer([]byte{}, 1, 0) }) vset = NewValidatorSet(nil) assert.Panics(t, func() { vset.IncrementProposerPriority(1) }) - assert.Panics(t, func() { SelectProposer(vset, []byte{}, 1, 0) }) + assert.Panics(t, func() { vset.SelectProposer([]byte{}, 1, 0) }) assert.EqualValues(t, vset, vset.Copy()) assert.False(t, vset.HasAddress([]byte("some val"))) @@ -65,7 +65,8 @@ func TestValidatorSetBasic(t *testing.T) { assert.Equal(t, val.VotingPower, vset.TotalVotingPower()) assert.NotNil(t, vset.Hash()) assert.NotPanics(t, func() { vset.IncrementProposerPriority(1) }) - assert.Equal(t, val.Address, SelectProposer(vset, []byte{}, 1, 0).Address) + assert.Equal(t, val.Address, + vset.SelectProposer([]byte{}, 1, 0).Address) // update val = randValidator(vset.TotalVotingPower()) @@ -135,14 +136,14 @@ func TestCopy(t *testing.T) { vset := randValidatorSet(10) vsetHash := vset.Hash() if len(vsetHash) == 0 { - t.Fatalf("ValidatorSet had unexpected zero hash") + t.Fatalf("VoterSet had unexpected zero hash") } vsetCopy := vset.Copy() vsetCopyHash := vsetCopy.Hash() if !bytes.Equal(vsetHash, vsetCopyHash) { - t.Fatalf("ValidatorSet copy had wrong hash. Orig: %X, Copy: %X", vsetHash, vsetCopyHash) + t.Fatalf("VoterSet copy had wrong hash. Orig: %X, Copy: %X", vsetHash, vsetCopyHash) } } @@ -196,7 +197,7 @@ func bytesToInt(b []byte) int { func verifyWinningRate(t *testing.T, vals *ValidatorSet, tries int, error float64) { selected := make([]int, len(vals.Validators)) for i := 0; i < tries; i++ { - prop := SelectProposer(vals, []byte{}, int64(i), 0) + prop := vals.SelectProposer([]byte{}, int64(i), 0) for j := 0; j < len(vals.Validators); j++ { if bytes.Equal(prop.Address, vals.Validators[j].Address) { selected[j]++ @@ -226,7 +227,7 @@ func TestProposerSelection1(t *testing.T) { }) var proposers []string for i := 0; i < 99; i++ { - val := SelectProposer(vset, []byte{}, int64(i), 0) + val := vset.SelectProposer([]byte{}, int64(i), 0) proposers = append(proposers, string(val.Address)) } expected := `foo foo foo foo bar bar foo bar foo baz bar foo baz baz baz foo foo bar foo bar baz bar foo baz foo ` + @@ -249,7 +250,7 @@ func TestProposerSelection2(t *testing.T) { vals := NewValidatorSet(valList) expected := []int{0, 1, 0, 0, 2, 2, 0, 2, 1, 2, 2, 1, 2, 2, 2} for i := 0; i < len(valList)*5; i++ { - prop := SelectProposer(vals, []byte{}, int64(i), 0) + prop := vals.SelectProposer([]byte{}, int64(i), 0) if bytesToInt(prop.Address) != expected[i] { t.Fatalf("(%d): Expected %d. Got %d", i, expected[i], bytesToInt(prop.Address)) } @@ -273,7 +274,7 @@ func TestProposerSelection2(t *testing.T) { vals = NewValidatorSet(valList) N := 4 + 5 + 3 for i := 0; i < 10000*N; i++ { - prop := SelectProposer(vals, []byte{}, int64(i), 0) + prop := vals.SelectProposer([]byte{}, int64(i), 0) propCount[bytesToInt(prop.Address)]++ } fmt.Printf("%v", propCount) @@ -317,10 +318,17 @@ func randPubKey() crypto.PubKey { return ed25519.PubKey(tmrand.Bytes(32)) } +func max(a, b int64) int64 { + if a >= b { + return a + } + return b +} + func randValidator(totalVotingPower int64) *Validator { // this modulo limits the ProposerPriority/VotingPower to stay in the // bounds of MaxTotalVotingPower minus the already existing voting power: - val := NewValidator(randPubKey(), int64(tmrand.Uint64()%uint64(MaxTotalVotingPower-totalVotingPower))) + val := NewValidator(randPubKey(), max(int64(tmrand.Uint64()%uint64(MaxTotalVotingPower-totalVotingPower)), 1)) val.ProposerPriority = tmrand.Int64() % (MaxTotalVotingPower - totalVotingPower) return val } @@ -335,6 +343,25 @@ func randValidatorSet(numValidators int) *ValidatorSet { return NewValidatorSet(validators) } +func randValidatorWithMinMax(min, max int64) (*Validator, PrivValidator) { + privVal := NewMockPV() + pubKey, _ := privVal.GetPubKey() + val := NewValidator(pubKey, min+int64(tmrand.Uint64()%uint64(1+max-min))) + val.ProposerPriority = min + tmrand.Int64()%max + return val, privVal +} + +func randValidatorSetWithMinMax(numValidators int, min, max int64) (*ValidatorSet, map[string]PrivValidator) { + validators := make([]*Validator, numValidators) + privMap := make(map[string]PrivValidator) + var privVal PrivValidator + for i := 0; i < numValidators; i++ { + validators[i], privVal = randValidatorWithMinMax(min, max) + privMap[validators[i].Address.String()] = privVal + } + return NewValidatorSet(validators), privMap +} + func (vals *ValidatorSet) toBytes() []byte { pbvs, err := vals.ToProto() if err != nil { @@ -571,7 +598,8 @@ func TestAveragingInIncrementProposerPriorityWithVotingPower(t *testing.T) { for i, tc := range tcs { tc.vals.IncrementProposerPriority(tc.times) - assert.Equal(t, tc.wantProposer.Address, SelectProposer(tc.vals, []byte{}, int64(i), 0).Address, + assert.Equal(t, tc.wantProposer.Address, + tc.vals.SelectProposer([]byte{}, int64(i), 0).Address, "test case: %v", i) for valIdx, val := range tc.vals.Validators { @@ -617,7 +645,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { privKey = ed25519.GenPrivKey() pubKey = privKey.PubKey() v1 = NewValidator(pubKey, 1000) - vset = NewValidatorSet([]*Validator{v1}) + vset = NewVoterSet([]*Validator{v1}) chainID = "Lalande21185" ) @@ -695,7 +723,7 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) { blockID = makeBlockIDRandom() ) - voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + voteSet, _, voterSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) @@ -707,7 +735,7 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) { vote.Signature = v.Signature commit.Signatures[3] = vote.CommitSig() - err = valSet.VerifyCommit(chainID, blockID, h, commit) + err = voterSet.VerifyCommit(chainID, blockID, h, commit) if assert.Error(t, err) { assert.Contains(t, err.Error(), "wrong signature (#3)") } @@ -720,7 +748,7 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign blockID = makeBlockIDRandom() ) - voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + voteSet, _, voterSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) @@ -732,7 +760,7 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign vote.Signature = v.Signature commit.Signatures[3] = vote.CommitSig() - err = valSet.VerifyCommitLight(chainID, blockID, h, commit) + err = voterSet.VerifyCommitLight(chainID, blockID, h, commit) assert.NoError(t, err) } @@ -743,7 +771,7 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin blockID = makeBlockIDRandom() ) - voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) + voteSet, _, voterSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, 10) commit, err := MakeCommit(blockID, h, 0, voteSet, vals, time.Now()) require.NoError(t, err) @@ -755,7 +783,7 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin vote.Signature = v.Signature commit.Signatures[2] = vote.CommitSig() - err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) + err = voterSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) assert.NoError(t, err) } @@ -1465,36 +1493,36 @@ func TestValSetUpdateOverflowRelated(t *testing.T) { func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { var ( - blockID = makeBlockIDRandom() - voteSet, originalValset, vals = randVoteSet(1, 1, tmproto.PrecommitType, 6, 1) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) - newValSet, _ = RandValidatorSet(2, 1) + blockID = makeBlockIDRandom() + voteSet, _, originalVoterSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 6, 1) + commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + _, newVoterSet, _ = RandVoterSet(2, 1) ) require.NoError(t, err) testCases := []struct { - valSet *ValidatorSet - err bool + voterSet *VoterSet + err bool }{ // good 0: { - valSet: originalValset, - err: false, + voterSet: originalVoterSet, + err: false, }, // bad - no overlap between validator sets 1: { - valSet: newValSet, - err: true, + voterSet: newVoterSet, + err: true, }, // good - first two are different but the rest of the same -> >1/3 2: { - valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)), - err: false, + voterSet: NewVoterSet(append(newVoterSet.Voters, originalVoterSet.Voters...)), + err: false, }, } for _, tc := range testCases { - err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit, + err = tc.voterSet.VerifyCommitLightTrusting("test_chain_id", commit, tmmath.Fraction{Numerator: 1, Denominator: 3}) if tc.err { assert.Error(t, err) @@ -1506,13 +1534,13 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) { func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) { var ( - blockID = makeBlockIDRandom() - voteSet, valSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower) - commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) + blockID = makeBlockIDRandom() + voteSet, _, voterSet, vals = randVoteSet(1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower) + commit, err = MakeCommit(blockID, 1, 1, voteSet, vals, time.Now()) ) require.NoError(t, err) - err = valSet.VerifyCommitLightTrusting("test_chain_id", commit, + err = voterSet.VerifyCommitLightTrusting("test_chain_id", commit, tmmath.Fraction{Numerator: 25, Denominator: 55}) if assert.Error(t, err) { assert.Contains(t, err.Error(), "int64 overflow") @@ -1546,8 +1574,8 @@ func TestSafeMul(t *testing.T) { } func TestValidatorSetProtoBuf(t *testing.T) { - valset, _ := RandValidatorSet(10, 100) - valset2, _ := RandValidatorSet(10, 100) + valset, _, _ := RandVoterSet(10, 100) + valset2, _, _ := RandVoterSet(10, 100) valset2.Validators[0] = &Validator{} testCases := []struct { diff --git a/types/vote_set.go b/types/vote_set.go index 42bde9856..edd33433e 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -63,7 +63,7 @@ type VoteSet struct { height int64 round int32 signedMsgType tmproto.SignedMsgType - valSet *ValidatorSet + valSet *VoterSet mtx tmsync.Mutex votesBitArray *bits.BitArray @@ -76,7 +76,7 @@ type VoteSet struct { // Constructs a new VoteSet struct used to accumulate votes for given height/round. func NewVoteSet(chainID string, height int64, round int32, - signedMsgType tmproto.SignedMsgType, valSet *ValidatorSet) *VoteSet { + signedMsgType tmproto.SignedMsgType, voterSet *VoterSet) *VoteSet { if height == 0 { panic("Cannot make VoteSet for height == 0, doesn't make sense.") } @@ -85,12 +85,12 @@ func NewVoteSet(chainID string, height int64, round int32, height: height, round: round, signedMsgType: signedMsgType, - valSet: valSet, - votesBitArray: bits.NewBitArray(valSet.Size()), - votes: make([]*Vote, valSet.Size()), + valSet: voterSet, + votesBitArray: bits.NewBitArray(voterSet.Size()), + votes: make([]*Vote, voterSet.Size()), sum: 0, maj23: nil, - votesByBlock: make(map[string]*blockVotes, valSet.Size()), + votesByBlock: make(map[string]*blockVotes, voterSet.Size()), peerMaj23s: make(map[P2PID]BlockID), } } @@ -176,10 +176,10 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { } // Ensure that signer is a validator. - lookupAddr, val := voteSet.valSet.GetByIndex(valIndex) + lookupAddr, val := voteSet.valSet.GetByIndex(int(valIndex)) if val == nil { return false, fmt.Errorf( - "cannot find validator %d in valSet of size %d: %w", + "cannot find voter %d in valSet of size %d: %w", valIndex, voteSet.valSet.Size(), ErrVoteInvalidValidatorIndex) } @@ -616,11 +616,11 @@ type blockVotes struct { sum int64 // vote sum } -func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes { +func newBlockVotes(peerMaj23 bool, numVoters int) *blockVotes { return &blockVotes{ peerMaj23: peerMaj23, - bitArray: bits.NewBitArray(numValidators), - votes: make([]*Vote, numValidators), + bitArray: bits.NewBitArray(numVoters), + votes: make([]*Vote, numVoters), sum: 0, } } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 4899b04b2..3a0fee798 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -15,7 +15,7 @@ import ( func TestVoteSet_AddVote_Good(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) val0 := privValidators[0] val0p, err := val0.GetPubKey() @@ -47,7 +47,7 @@ func TestVoteSet_AddVote_Good(t *testing.T) { func TestVoteSet_AddVote_Bad(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) voteProto := &Vote{ ValidatorAddress: nil, @@ -122,7 +122,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { func TestVoteSet_2_3Majority(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, 1) voteProto := &Vote{ ValidatorAddress: nil, // NOTE: must fill in @@ -172,7 +172,7 @@ func TestVoteSet_2_3Majority(t *testing.T) { func TestVoteSet_2_3MajorityRedux(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 100, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 100, 1) blockHash := crypto.CRandBytes(32) blockPartsTotal := uint32(123) @@ -271,7 +271,8 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) { func TestVoteSet_Conflicts(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 4, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 4, 1) + blockHash1 := tmrand.Bytes(32) blockHash2 := tmrand.Bytes(32) @@ -400,7 +401,7 @@ func TestVoteSet_Conflicts(t *testing.T) { func TestVoteSet_MakeCommit(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrecommitType, 10, 1) + voteSet, _, _, privValidators := randVoteSet(height, round, tmproto.PrecommitType, 10, 1) blockHash, blockPartSetHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)} voteProto := &Vote{ @@ -481,9 +482,9 @@ func randVoteSet( signedMsgType tmproto.SignedMsgType, numValidators int, votingPower int64, -) (*VoteSet, *ValidatorSet, []PrivValidator) { - valSet, privValidators := RandValidatorSet(numValidators, votingPower) - return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators +) (*VoteSet, *ValidatorSet, *VoterSet, []PrivValidator) { + valSet, voterSet, privValidators := RandVoterSet(numValidators, votingPower) + return NewVoteSet("test_chain_id", height, round, signedMsgType, voterSet), valSet, voterSet, privValidators } // Convenience: Return new vote with different validator address/index diff --git a/types/voter_set.go b/types/voter_set.go new file mode 100644 index 000000000..aa1843a2b --- /dev/null +++ b/types/voter_set.go @@ -0,0 +1,537 @@ +package types + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" + "sort" + "strings" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/pkg/errors" + + "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/crypto/tmhash" + tmmath "github.com/tendermint/tendermint/libs/math" + tmrand "github.com/tendermint/tendermint/libs/rand" +) + +var MaxVoters = 20 + +// VoterSet represent a set of *Validator at a given height. +type VoterSet struct { + // NOTE: persisted via reflect, must be exported. + Voters []*Validator `json:"voters"` + + // cached (unexported) + totalVotingPower int64 +} + +func NewVoterSet(valz []*Validator) *VoterSet { + sort.Sort(ValidatorsByAddress(valz)) + vals := &VoterSet{Voters: copyValidatorListShallow(valz), totalVotingPower: 0} + vals.updateTotalVotingPower() + return vals +} + +func (voters *VoterSet) ValidateBasic() error { + if voters.IsNilOrEmpty() { + return errors.New("voter set is nil or empty") + } + + for idx, val := range voters.Voters { + if err := val.ValidateBasic(); err != nil { + return fmt.Errorf("invalid validator #%d: %w", idx, err) + } + } + + return nil +} + +// IsNilOrEmpty returns true if validator set is nil or empty. +func (voters *VoterSet) IsNilOrEmpty() bool { + return voters == nil || len(voters.Voters) == 0 +} + +// HasAddress returns true if address given is in the validator set, false - +// otherwise. +func (voters *VoterSet) HasAddress(address []byte) bool { + idx := sort.Search(len(voters.Voters), func(i int) bool { + return bytes.Compare(address, voters.Voters[i].Address) <= 0 + }) + return idx < len(voters.Voters) && bytes.Equal(voters.Voters[idx].Address, address) +} + +// GetByAddress returns an index of the validator with address and validator +// itself if found. Otherwise, -1 and nil are returned. +func (voters *VoterSet) GetByAddress(address []byte) (index int, val *Validator) { + idx := sort.Search(len(voters.Voters), func(i int) bool { + return bytes.Compare(address, voters.Voters[i].Address) <= 0 + }) + if idx < len(voters.Voters) && bytes.Equal(voters.Voters[idx].Address, address) { + return idx, voters.Voters[idx].Copy() + } + return -1, nil +} + +// GetByIndex returns the validator's address and validator itself by index. +// It returns nil values if index is less than 0 or greater or equal to +// len(VoterSet.Validators). +func (voters *VoterSet) GetByIndex(index int) (address []byte, val *Validator) { + if index < 0 || index >= len(voters.Voters) { + return nil, nil + } + val = voters.Voters[index] + return val.Address, val.Copy() +} + +// Size returns the length of the validator set. +func (voters *VoterSet) Size() int { + return len(voters.Voters) +} + +func copyValidatorListShallow(vals []*Validator) []*Validator { + result := make([]*Validator, len(vals)) + copy(result, vals) + return result +} + +// VoterSet.Copy() copies validator list shallow +func (voters *VoterSet) Copy() *VoterSet { + return &VoterSet{ + Voters: copyValidatorListShallow(voters.Voters), + totalVotingPower: voters.totalVotingPower, + } +} + +// Forces recalculation of the set's total voting power. +// Panics if total voting power is bigger than MaxTotalVotingPower. +func (voters *VoterSet) updateTotalVotingPower() { + sum := int64(0) + for _, val := range voters.Voters { + // mind overflow + sum = safeAddClip(sum, val.VotingPower) + if sum > MaxTotalVotingPower { + panic(fmt.Sprintf( + "Total voting power should be guarded to not exceed %v; got: %v", + MaxTotalVotingPower, + sum)) + } + } + + voters.totalVotingPower = sum +} + +func (voters *VoterSet) TotalVotingPower() int64 { + if voters.totalVotingPower == 0 { + voters.updateTotalVotingPower() + } + return voters.totalVotingPower +} + +// Hash returns the Merkle root hash build using validators (as leaves) in the +// set. +func (voters *VoterSet) Hash() []byte { + if len(voters.Voters) == 0 { + return nil + } + bzs := make([][]byte, len(voters.Voters)) + for i, val := range voters.Voters { + bzs[i] = val.Bytes() + } + return merkle.HashFromByteSlices(bzs) +} + +// VerifyCommit verifies +2/3 of the set had signed the given commit. +// +// It checks all the signatures! While it's safe to exit as soon as we have +// 2/3+ signatures, doing so would impact incentivization logic in the ABCI +// application that depends on the LastCommitInfo sent in BeginBlock, which +// includes which validators signed. For instance, Gaia incentivizes proposers +// with a bonus for including more than +2/3 of the signatures. +func (voters *VoterSet) VerifyCommit(chainID string, blockID BlockID, + height int64, commit *Commit) error { + + if voters.Size() != len(commit.Signatures) { + return NewErrInvalidCommitSignatures(voters.Size(), len(commit.Signatures)) + } + + // Validate Height and BlockID. + if height != commit.Height { + return NewErrInvalidCommitHeight(height, commit.Height) + } + if !blockID.Equals(commit.BlockID) { + return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", + blockID, commit.BlockID) + } + + talliedVotingPower := int64(0) + votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 + for idx, commitSig := range commit.Signatures { + if commitSig.Absent() { + continue // OK, some signatures can be absent. + } + + // The vals and commit have a 1-to-1 correspondance. + // This means we don't need the validator address or to do any lookup. + val := voters.Voters[idx] + + // Validate signature. + voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) + if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + // Good! + if blockID.Equals(commitSig.BlockID(commit.BlockID)) { + talliedVotingPower += val.VotingPower + } + // else { + // It's OK that the BlockID doesn't match. We include stray + // signatures (~votes for nil) to measure validator availability. + // } + } + + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { + return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + } + + return nil +} + +// LIGHT CLIENT VERIFICATION METHODS + +// VerifyCommitLight verifies +2/3 of the set had signed the given commit. +// +// This method is primarily used by the light client and does not check all the +// signatures. +func (voters *VoterSet) VerifyCommitLight(chainID string, blockID BlockID, + height int64, commit *Commit) error { + + if voters.Size() != len(commit.Signatures) { + return NewErrInvalidCommitSignatures(voters.Size(), len(commit.Signatures)) + } + + // Validate Height and BlockID. + if height != commit.Height { + return NewErrInvalidCommitHeight(height, commit.Height) + } + if !blockID.Equals(commit.BlockID) { + return fmt.Errorf("invalid commit -- wrong block ID: want %v, got %v", + blockID, commit.BlockID) + } + + talliedVotingPower := int64(0) + votingPowerNeeded := voters.TotalVotingPower() * 2 / 3 + for idx, commitSig := range commit.Signatures { + // No need to verify absent or nil votes. + if !commitSig.ForBlock() { + continue + } + + // The vals and commit have a 1-to-1 correspondance. + // This means we don't need the validator address or to do any lookup. + val := voters.Voters[idx] + + // Validate signature. + voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) + if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + + talliedVotingPower += val.VotingPower + + // return as soon as +2/3 of the signatures are verified + if talliedVotingPower > votingPowerNeeded { + return nil + } + } + + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} +} + +// VerifyCommitLightTrusting verifies that trustLevel of the validator set signed +// this commit. +// +// NOTE the given validators do not necessarily correspond to the validator set +// for this commit, but there may be some intersection. +// +// This method is primarily used by the light client and does not check all the +// signatures. +func (voters *VoterSet) VerifyCommitLightTrusting(chainID string, commit *Commit, trustLevel tmmath.Fraction) error { + // sanity check + if trustLevel.Denominator == 0 { + return errors.New("trustLevel has zero Denominator") + } + + var ( + talliedVotingPower int64 + seenVals = make(map[int32]int, len(commit.Signatures)) // validator index -> commit index + ) + + // Safely calculate voting power needed. + totalVotingPowerMulByNumerator, overflow := safeMul(voters.TotalVotingPower(), int64(trustLevel.Numerator)) + if overflow { + return errors.New("int64 overflow while calculating voting power needed. please provide smaller trustLevel numerator") + } + votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) + + for idx, commitSig := range commit.Signatures { + // No need to verify absent or nil votes. + if !commitSig.ForBlock() { + continue + } + + // We don't know the validators that committed this block, so we have to + // check for each vote if its validator is already known. + valIdx, val := voters.GetByAddress(commitSig.ValidatorAddress) + + if val != nil { + // check for double vote of validator on the same commit + if firstIndex, ok := seenVals[int32(valIdx)]; ok { + secondIndex := idx + return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) + } + seenVals[int32(valIdx)] = idx + + // Validate signature. + voteSignBytes := commit.VoteSignBytes(chainID, int32(idx)) + if !val.PubKey.VerifySignature(voteSignBytes, commitSig.Signature) { + return fmt.Errorf("wrong signature (#%d): %X", idx, commitSig.Signature) + } + + talliedVotingPower += val.VotingPower + + if talliedVotingPower > votingPowerNeeded { + return nil + } + } + } + + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} +} + +// findPreviousProposer reverses the compare proposer priority function to find the validator +// with the lowest proposer priority which would have been the previous proposer. +// +// Is used when recreating a validator set from an existing array of validators. +func (voters *VoterSet) findPreviousProposer() *Validator { + var previousProposer *Validator + for _, val := range voters.Voters { + if previousProposer == nil { + previousProposer = val + continue + } + if previousProposer == previousProposer.CompareProposerPriority(val) { + previousProposer = val + } + } + return previousProposer +} + +//----------------- + +// IsErrNotEnoughVotingPowerSigned returns true if err is +// ErrNotEnoughVotingPowerSigned. +func IsErrNotEnoughVotingPowerSigned(err error) bool { + return errors.As(err, &ErrNotEnoughVotingPowerSigned{}) +} + +// ErrNotEnoughVotingPowerSigned is returned when not enough validators signed +// a commit. +type ErrNotEnoughVotingPowerSigned struct { + Got int64 + Needed int64 +} + +func (e ErrNotEnoughVotingPowerSigned) Error() string { + return fmt.Sprintf("invalid commit -- insufficient voting power: got %d, needed more than %d", e.Got, e.Needed) +} + +//---------------- + +// Iterate will run the given function over the set. +func (voters *VoterSet) Iterate(fn func(index int, val *Validator) bool) { + for i, val := range voters.Voters { + stop := fn(i, val) + if stop { + break + } + } +} + +func (voters *VoterSet) String() string { + return voters.StringIndented("") +} + +// StringIndented returns an intended string representation of VoterSet. +func (voters *VoterSet) StringIndented(indent string) string { + if voters == nil { + return "nil-VoterSet" + } + var valStrings []string + voters.Iterate(func(index int, val *Validator) bool { + valStrings = append(valStrings, val.String()) + return false + }) + return fmt.Sprintf(`VoterSet{ +%s Validators: +%s %v +%s}`, + indent, indent, strings.Join(valStrings, "\n"+indent+" "), + indent) + +} + +func SelectVoter(validators *ValidatorSet, proofHash []byte) *VoterSet { + // TODO: decide MaxVoters; make it to config + if len(proofHash) == 0 || validators.Size() <= MaxVoters { + // height 1 has voter set that is same to validator set + result := &VoterSet{Voters: copyValidatorListShallow(validators.Validators), totalVotingPower: 0} + result.updateTotalVotingPower() + return result + } + + seed := hashToSeed(proofHash) + candidates := make([]tmrand.Candidate, len(validators.Validators)) + for i, val := range validators.Validators { + candidates[i] = &candidate{idx: i, win: 0, val: val} + } + totalSampling := tmrand.RandomSamplingToMax(seed, candidates, MaxVoters, uint64(validators.TotalVotingPower())) + voters := 0 + for _, candi := range candidates { + if candi.(*candidate).win > 0 { + voters += 1 + } + } + + vals := make([]*Validator, voters) + index := 0 + for _, candi := range candidates { + if candi.(*candidate).win > 0 { + vals[index] = &Validator{Address: candi.(*candidate).val.Address, + PubKey: candi.(*candidate).val.PubKey, + // VotingPower = TotalVotingPower * win / totalSampling : can be overflow + VotingPower: validators.TotalVotingPower()/int64(totalSampling)*int64(candi.(*candidate).win) + + int64(math.Ceil(float64(validators.TotalVotingPower()%int64(totalSampling))/float64(int64(totalSampling))* + float64(candi.(*candidate).win)))} + index++ + } + } + return NewVoterSet(vals) +} + +// ToVoterAll should be used in only test +func ToVoterAll(validators *ValidatorSet) *VoterSet { + return NewVoterSet(validators.Validators) +} + +// candidate save simple validator data for selecting proposer +type candidate struct { + idx int + win uint64 + val *Validator +} + +func (c *candidate) Priority() uint64 { + // TODO Is it possible to have a negative VotingPower? + if c.val.VotingPower < 0 { + return 0 + } + return uint64(c.val.VotingPower) +} + +func (c *candidate) LessThan(other tmrand.Candidate) bool { + o, ok := other.(*candidate) + if !ok { + panic("incompatible type") + } + return bytes.Compare(c.val.Address, o.val.Address) < 0 +} + +func (c *candidate) IncreaseWin() { + c.win++ +} + +func hashToSeed(hash []byte) uint64 { + for len(hash) < 8 { + hash = append(hash, byte(0)) + } + return binary.LittleEndian.Uint64(hash[:8]) +} + +// MakeRoundHash combines the VRF hash, block height, and round to create a hash value for each round. This value is +// used for random sampling of the Proposer. +func MakeRoundHash(proofHash []byte, height int64, round int32) []byte { + b := make([]byte, 16) + binary.LittleEndian.PutUint64(b, uint64(height)) + binary.LittleEndian.PutUint64(b[8:], uint64(round)) + hash := tmhash.New() + hash.Write(proofHash) + hash.Write(b[:8]) + hash.Write(b[8:16]) + return hash.Sum(nil) +} + +// RandVoterSet returns a randomized validator set (size: +numValidators+), +// where each validator has a voting power of +votingPower+. +// +// EXPOSED FOR TESTING. +func RandVoterSet(numVoters int, votingPower int64) (*ValidatorSet, *VoterSet, []PrivValidator) { + valz := make([]*Validator, numVoters) + privValidators := make([]PrivValidator, numVoters) + for i := 0; i < numVoters; i++ { + val, privValidator := RandValidator(false, votingPower) + valz[i] = val + privValidators[i] = privValidator + } + vals := NewValidatorSet(valz) + sort.Sort(PrivValidatorsByAddress(privValidators)) + return vals, SelectVoter(vals, []byte{}), privValidators +} + +// ToProto converts VoterSet to protobuf +func (voters *VoterSet) ToProto() (*tmproto.VoterSet, error) { + if voters.IsNilOrEmpty() { + return &tmproto.VoterSet{}, nil // validator set should never be nil + } + + vp := new(tmproto.VoterSet) + valsProto := make([]*tmproto.Validator, len(voters.Voters)) + for i := 0; i < len(voters.Voters); i++ { + valp, err := voters.Voters[i].ToProto() + if err != nil { + return nil, err + } + valsProto[i] = valp + } + vp.Validators = valsProto + + vp.TotalVotingPower = voters.totalVotingPower + + return vp, nil +} + +// VoterSetFromProto sets a protobuf VoterSet to the given pointer. +// It returns an error if any of the validators from the set is invalid +func VoterSetFromProto(vp *tmproto.VoterSet) (*VoterSet, error) { + if vp == nil { + return nil, errors.New("nil validator set") // validator set should never be nil, bigger issues are at play if empty + } + voters := new(VoterSet) + + valsProto := make([]*Validator, len(vp.Validators)) + for i := 0; i < len(vp.Validators); i++ { + v, err := ValidatorFromProto(vp.Validators[i]) + if err != nil { + return nil, err + } + valsProto[i] = v + } + voters.Voters = valsProto + + voters.totalVotingPower = vp.GetTotalVotingPower() + + return voters, voters.ValidateBasic() +} diff --git a/types/voter_set_test.go b/types/voter_set_test.go new file mode 100644 index 000000000..0ef757315 --- /dev/null +++ b/types/voter_set_test.go @@ -0,0 +1,113 @@ +package types + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/tendermint/tendermint/crypto/vrf" + tmtime "github.com/tendermint/tendermint/types/time" +) + +func TestSelectVoter(t *testing.T) { + MaxVoters = 29 + valSet := randValidatorSet(30) + for i := 0; i < 10000; i++ { + voterSet := SelectVoter(valSet, []byte{byte(i)}) + assert.True(t, math.Abs(float64(valSet.TotalVotingPower()-voterSet.TotalVotingPower())) <= 10) + } +} + +func toGenesisValidators(vals []*Validator) []GenesisValidator { + genVals := make([]GenesisValidator, len(vals)) + for i, val := range vals { + genVals[i] = GenesisValidator{Address: val.Address, PubKey: val.PubKey, Power: val.VotingPower, Name: "name"} + } + return genVals +} + +/** +The result when we set LoopCount to 10000 + << min power=100, max power=100, actual average voters=10, max voters=10 >> largest gap: 0.040000 + << min power=100, max power=100, actual average voters=20, max voters=20 >> largest gap: 0.030000 + << min power=100, max power=100, actual average voters=29, max voters=29 >> largest gap: 0.010000 + << min power=100, max power=10000, actual average voters=10, max voters=10 >> largest gap: 0.183673 + << min power=100, max power=10000, actual average voters=20, max voters=20 >> largest gap: 0.128788 + << min power=100, max power=10000, actual average voters=28, max voters=29 >> largest gap: 0.304348 + << min power=100, max power=1000000, actual average voters=10, max voters=10 >> largest gap: 0.093158 + << min power=100, max power=1000000, actual average voters=20, max voters=20 >> largest gap: 0.094404 + << min power=100, max power=1000000, actual average voters=28, max voters=29 >> largest gap: 0.194133 + << min power=100, max power=100000000, actual average voters=10, max voters=10 >> largest gap: 0.076536 + << min power=100, max power=100000000, actual average voters=20, max voters=20 >> largest gap: 0.076547 + << min power=100, max power=100000000, actual average voters=29, max voters=29 >> largest gap: 0.147867 +*/ +func TestSelectVoterReasonableVotingPower(t *testing.T) { + // Raise LoopCount to get smaller gap over 10000. But large LoopCount takes a lot of time + const LoopCount = 100 + for minMaxRate := 1; minMaxRate <= 1000000; minMaxRate *= 100 { + findLargestVotingPowerGap(t, LoopCount, minMaxRate, 10) + findLargestVotingPowerGap(t, LoopCount, minMaxRate, 20) + findLargestVotingPowerGap(t, LoopCount, minMaxRate, 29) + } +} + +func findLargestVotingPowerGap(t *testing.T, loopCount int, minMaxRate int, maxVoters int) { + valSet, privMap := randValidatorSetWithMinMax(30, 100, 100*int64(minMaxRate)) + genDoc := &GenesisDoc{ + GenesisTime: tmtime.Now(), + ChainID: "tendermint-test", + Validators: toGenesisValidators(valSet.Validators), + } + hash := genDoc.Hash() + MaxVoters = maxVoters + accumulation := make(map[string]int64) + totalVoters := 0 + for i := 0; i < loopCount; i++ { + voterSet := SelectVoter(valSet, hash) + for _, voter := range voterSet.Voters { + accumulation[voter.Address.String()] += voter.VotingPower + } + proposer := valSet.SelectProposer(hash, int64(i), 0) + message := MakeRoundHash(hash, int64(i), 0) + proof, _ := privMap[proposer.Address.String()].GenerateVRFProof(message) + hash, _ = vrf.ProofToHash(proof) + totalVoters += voterSet.Size() + } + largestGap := float64(0) + for _, val := range valSet.Validators { + acc := accumulation[val.Address.String()] / int64(loopCount) + if math.Abs(float64(val.VotingPower-acc))/float64(val.VotingPower) > largestGap { + largestGap = math.Abs(float64(val.VotingPower-acc)) / float64(val.VotingPower) + } + } + t.Logf("<< min power=100, max power=%d, actual average voters=%d, max voters=%d >> largest gap: %f", + 100*minMaxRate, totalVoters/loopCount, maxVoters, largestGap) +} + +/** + This test is a test to see the difference between MaxVoters and the actual number of elected voters. + This test is to identify the minimum MaxVoters that cannot be selected as much as MaxVoters by fixing MaxSamplingLoopTry. + If MaxSamplingLoopTry is very large then actual elected voters is up to MaxVoters, + but large MaxSamplingLoopTry takes too much time. +*/ +func TestSelectVoterMaxVarious(t *testing.T) { + hash := 0 + for minMaxRate := 1; minMaxRate <= 100000000; minMaxRate *= 10000 { + t.Logf("<<< min: 100, max: %d >>>", 100*minMaxRate) + for validators := 16; validators <= 256; validators *= 4 { + for voters := 1; voters <= validators; voters += 10 { + MaxVoters = voters + valSet, _ := randValidatorSetWithMinMax(validators, 100, 100*int64(minMaxRate)) + voterSet := SelectVoter(valSet, []byte{byte(hash)}) + assert.True(t, int(math.Abs(float64(valSet.TotalVotingPower()-voterSet.TotalVotingPower()))) <= voters) + if voterSet.Size() < MaxVoters { + t.Logf("Cannot elect voters up to MaxVoters: validators=%d, MaxVoters=%d, actual voters=%d", + validators, voters, voterSet.Size()) + break + } + hash++ + } + } + } +}