Browse Source

IncrementAccum upon RPC /validators; Sanity checks and comments (#2808)

pull/2903/head
Jae Kwon 6 years ago
committed by Anton Kaliaev
parent
commit
42592d9ae0
6 changed files with 59 additions and 9 deletions
  1. +2
    -0
      CHANGELOG_PENDING.md
  2. +2
    -1
      state/state.go
  3. +23
    -1
      state/state_test.go
  4. +14
    -6
      state/store.go
  5. +4
    -0
      types/validator_set.go
  6. +14
    -1
      types/validator_set_test.go

+ 2
- 0
CHANGELOG_PENDING.md View File

@ -30,3 +30,5 @@ program](https://hackerone.com/tendermint).
- [mempool] \#2835 Remove local int64 counter from being stored in every tx - [mempool] \#2835 Remove local int64 counter from being stored in every tx
### BUG FIXES: ### BUG FIXES:
- [rpc] \#2808 RPC validators calls IncrementAccum if necessary

+ 2
- 1
state/state.go View File

@ -64,7 +64,8 @@ type State struct {
// Validators are persisted to the database separately every time they change, // Validators are persisted to the database separately every time they change,
// so we can query for historical validator sets. // so we can query for historical validator sets.
// Note that if s.LastBlockHeight causes a valset change, // Note that if s.LastBlockHeight causes a valset change,
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1
// we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1
// Extra +1 due to nextValSet delay.
NextValidators *types.ValidatorSet NextValidators *types.ValidatorSet
Validators *types.ValidatorSet Validators *types.ValidatorSet
LastValidators *types.ValidatorSet LastValidators *types.ValidatorSet


+ 23
- 1
state/state_test.go View File

@ -3,9 +3,10 @@ package state
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/tendermint/tendermint/libs/log"
"testing" "testing"
"github.com/tendermint/tendermint/libs/log"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -260,6 +261,27 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) {
} }
} }
func TestStoreLoadValidatorsIncrementsAccum(t *testing.T) {
const valSetSize = 2
tearDown, stateDB, state := setupTestCase(t)
state.Validators = genValSet(valSetSize)
state.NextValidators = state.Validators.CopyIncrementAccum(1)
SaveState(stateDB, state)
defer tearDown(t)
nextHeight := state.LastBlockHeight + 1
v0, err := LoadValidators(stateDB, nextHeight)
assert.Nil(t, err)
acc0 := v0.Validators[0].Accum
v1, err := LoadValidators(stateDB, nextHeight+1)
assert.Nil(t, err)
acc1 := v1.Validators[0].Accum
assert.NotEqual(t, acc1, acc0, "expected Accum value to change between heights")
}
// TestValidatorChangesSaveLoad tests saving and loading a validator set with // TestValidatorChangesSaveLoad tests saving and loading a validator set with
// changes. // changes.
func TestManyValidatorChangesSaveLoad(t *testing.T) { func TestManyValidatorChangesSaveLoad(t *testing.T) {


+ 14
- 6
state/store.go View File

@ -89,7 +89,9 @@ func saveState(db dbm.DB, state State, key []byte) {
nextHeight := state.LastBlockHeight + 1 nextHeight := state.LastBlockHeight + 1
// If first block, save validators for block 1. // If first block, save validators for block 1.
if nextHeight == 1 { if nextHeight == 1 {
lastHeightVoteChanged := int64(1) // Due to Tendermint validator set changes being delayed 1 block.
// This extra logic due to Tendermint validator set changes being delayed 1 block.
// It may get overwritten due to InitChain validator updates.
lastHeightVoteChanged := int64(1)
saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators) saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators)
} }
// Save next validators. // Save next validators.
@ -191,12 +193,14 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) {
), ),
) )
} }
valInfo2.ValidatorSet.IncrementAccum(int(height - valInfo.LastHeightChanged)) // mutate
valInfo = valInfo2 valInfo = valInfo2
} }
return valInfo.ValidatorSet, nil return valInfo.ValidatorSet, nil
} }
// CONTRACT: Returned ValidatorsInfo can be mutated.
func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
buf := db.Get(calcValidatorsKey(height)) buf := db.Get(calcValidatorsKey(height))
if len(buf) == 0 { if len(buf) == 0 {
@ -215,18 +219,22 @@ func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo {
return v return v
} }
// saveValidatorsInfo persists the validator set for the next block to disk.
// saveValidatorsInfo persists the validator set.
// `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. // It should be called from s.Save(), right before the state itself is persisted.
// If the validator set did not change after processing the latest block, // If the validator set did not change after processing the latest block,
// only the last height for which the validators changed is persisted. // only the last height for which the validators changed is persisted.
func saveValidatorsInfo(db dbm.DB, nextHeight, changeHeight int64, valSet *types.ValidatorSet) {
func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) {
if lastHeightChanged > height {
panic("LastHeightChanged cannot be greater than ValidatorsInfo height")
}
valInfo := &ValidatorsInfo{ valInfo := &ValidatorsInfo{
LastHeightChanged: changeHeight,
LastHeightChanged: lastHeightChanged,
} }
if changeHeight == nextHeight {
if lastHeightChanged == height {
valInfo.ValidatorSet = valSet valInfo.ValidatorSet = valSet
} }
db.Set(calcValidatorsKey(nextHeight), valInfo.Bytes())
db.Set(calcValidatorsKey(height), valInfo.Bytes())
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------


+ 4
- 0
types/validator_set.go View File

@ -62,7 +62,11 @@ func (vals *ValidatorSet) CopyIncrementAccum(times int) *ValidatorSet {
// IncrementAccum increments accum of each validator and updates the // IncrementAccum increments accum of each validator and updates the
// proposer. Panics if validator set is empty. // proposer. Panics if validator set is empty.
// `times` must be positive.
func (vals *ValidatorSet) IncrementAccum(times int) { func (vals *ValidatorSet) IncrementAccum(times int) {
if times <= 0 {
panic("Cannot call IncrementAccum with non-positive times")
}
// Add VotingPower * times to each validator and order into heap. // Add VotingPower * times to each validator and order into heap.
validatorsHeap := cmn.NewHeap() validatorsHeap := cmn.NewHeap()


+ 14
- 1
types/validator_set_test.go View File

@ -86,6 +86,19 @@ func TestCopy(t *testing.T) {
} }
} }
// Test that IncrementAccum requires positive times.
func TestIncrementAccumPositiveTimes(t *testing.T) {
vset := NewValidatorSet([]*Validator{
newValidator([]byte("foo"), 1000),
newValidator([]byte("bar"), 300),
newValidator([]byte("baz"), 330),
})
assert.Panics(t, func() { vset.IncrementAccum(-1) })
assert.Panics(t, func() { vset.IncrementAccum(0) })
vset.IncrementAccum(1)
}
func BenchmarkValidatorSetCopy(b *testing.B) { func BenchmarkValidatorSetCopy(b *testing.B) {
b.StopTimer() b.StopTimer()
vset := NewValidatorSet([]*Validator{}) vset := NewValidatorSet([]*Validator{})
@ -239,7 +252,7 @@ func TestProposerSelection3(t *testing.T) {
mod := (cmn.RandInt() % 5) + 1 mod := (cmn.RandInt() % 5) + 1
if cmn.RandInt()%mod > 0 { if cmn.RandInt()%mod > 0 {
// sometimes its up to 5 // sometimes its up to 5
times = cmn.RandInt() % 5
times = (cmn.RandInt() % 4) + 1
} }
vset.IncrementAccum(times) vset.IncrementAccum(times)


Loading…
Cancel
Save