@ -45,7 +45,8 @@ func TestValidatorSetBasic(t *testing.T) {
assert . Nil ( t , vset . Hash ( ) )
// add
val = randValidator_ ( )
val = randValidator_ ( vset . TotalVotingPower ( ) )
assert . True ( t , vset . Add ( val ) )
assert . True ( t , vset . HasAddress ( val . Address ) )
idx , val2 := vset . GetByAddress ( val . Address )
@ -61,7 +62,7 @@ func TestValidatorSetBasic(t *testing.T) {
assert . NotPanics ( t , func ( ) { vset . IncrementProposerPriority ( 1 ) } )
// update
assert . False ( t , vset . Update ( randValidator_ ( ) ) )
assert . False ( t , vset . Update ( randValidator_ ( vset . TotalVotingPower ( ) ) ) )
_ , val = vset . GetByAddress ( val . Address )
val . VotingPower += 100
proposerPriority := val . ProposerPriority
@ -73,7 +74,7 @@ func TestValidatorSetBasic(t *testing.T) {
assert . Equal ( t , proposerPriority , val . ProposerPriority )
// remove
val2 , removed := vset . Remove ( randValidator_ ( ) . Address )
val2 , removed := vset . Remove ( randValidator_ ( vset . TotalVotingPower ( ) ) . Address )
assert . Nil ( t , val2 )
assert . False ( t , removed )
val2 , removed = vset . Remove ( val . Address )
@ -280,16 +281,20 @@ func randPubKey() crypto.PubKey {
return ed25519 . PubKeyEd25519 ( pubKey )
}
func randValidator_ ( ) * Validator {
val := NewValidator ( randPubKey ( ) , cmn . RandInt64 ( ) )
val . ProposerPriority = cmn . RandInt64 ( ) % MaxTotalVotingPower
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 ( ) , cmn . RandInt64 ( ) % ( MaxTotalVotingPower - totalVotingPower ) )
val . ProposerPriority = cmn . RandInt64 ( ) % ( MaxTotalVotingPower - totalVotingPower )
return val
}
func randValidatorSet ( numValidators int ) * ValidatorSet {
validators := make ( [ ] * Validator , numValidators )
totalVotingPower := int64 ( 0 )
for i := 0 ; i < numValidators ; i ++ {
validators [ i ] = randValidator_ ( )
validators [ i ] = randValidator_ ( totalVotingPower )
totalVotingPower += validators [ i ] . VotingPower
}
return NewValidatorSet ( validators )
}
@ -342,7 +347,174 @@ func TestAvgProposerPriority(t *testing.T) {
got := tc . vs . computeAvgProposerPriority ( )
assert . Equal ( t , tc . want , got , "test case: %v" , i )
}
}
func TestAveragingInIncrementProposerPriority ( t * testing . T ) {
// Test that the averaging works as expected inside of IncrementProposerPriority.
// Each validator comes with zero voting power which simplifies reasoning about
// the expected ProposerPriority.
tcs := [ ] struct {
vs ValidatorSet
times int
avg int64
} {
0 : { ValidatorSet {
Validators : [ ] * Validator {
{ Address : [ ] byte ( "a" ) , ProposerPriority : 1 } ,
{ Address : [ ] byte ( "b" ) , ProposerPriority : 2 } ,
{ Address : [ ] byte ( "c" ) , ProposerPriority : 3 } } } ,
1 , 2 } ,
1 : { ValidatorSet {
Validators : [ ] * Validator {
{ Address : [ ] byte ( "a" ) , ProposerPriority : 10 } ,
{ Address : [ ] byte ( "b" ) , ProposerPriority : - 10 } ,
{ Address : [ ] byte ( "c" ) , ProposerPriority : 1 } } } ,
// this should average twice but the average should be 0 after the first iteration
// (voting power is 0 -> no changes)
11 , 1 / 3 } ,
2 : { ValidatorSet {
Validators : [ ] * Validator {
{ Address : [ ] byte ( "a" ) , ProposerPriority : 100 } ,
{ Address : [ ] byte ( "b" ) , ProposerPriority : - 10 } ,
{ Address : [ ] byte ( "c" ) , ProposerPriority : 1 } } } ,
1 , 91 / 3 } ,
}
for i , tc := range tcs {
// work on copy to have the old ProposerPriorities:
newVset := tc . vs . CopyIncrementProposerPriority ( tc . times )
for _ , val := range tc . vs . Validators {
_ , updatedVal := newVset . GetByAddress ( val . Address )
assert . Equal ( t , updatedVal . ProposerPriority , val . ProposerPriority - tc . avg , "test case: %v" , i )
}
}
}
func TestAveragingInIncrementProposerPriorityWithVotingPower ( t * testing . T ) {
// Other than TestAveragingInIncrementProposerPriority this is a more complete test showing
// how each ProposerPriority changes in relation to the validator's voting power respectively.
vals := ValidatorSet { Validators : [ ] * Validator {
{ Address : [ ] byte { 0 } , ProposerPriority : 0 , VotingPower : 10 } ,
{ Address : [ ] byte { 1 } , ProposerPriority : 0 , VotingPower : 1 } ,
{ Address : [ ] byte { 2 } , ProposerPriority : 0 , VotingPower : 1 } } }
tcs := [ ] struct {
vals * ValidatorSet
wantProposerPrioritys [ ] int64
times int
wantProposer * Validator
} {
0 : {
vals . Copy ( ) ,
[ ] int64 {
// Acumm+VotingPower-Avg:
0 + 10 - 12 - 4 , // mostest will be subtracted by total voting power (12)
0 + 1 - 4 ,
0 + 1 - 4 } ,
1 ,
vals . Validators [ 0 ] } ,
1 : {
vals . Copy ( ) ,
[ ] int64 {
( 0 + 10 - 12 - 4 ) + 10 - 12 + 4 , // this will be mostest on 2nd iter, too
( 0 + 1 - 4 ) + 1 + 4 ,
( 0 + 1 - 4 ) + 1 + 4 } ,
2 ,
vals . Validators [ 0 ] } , // increment twice -> expect average to be subtracted twice
2 : {
vals . Copy ( ) ,
[ ] int64 {
( ( 0 + 10 - 12 - 4 ) + 10 - 12 ) + 10 - 12 + 4 , // still mostest
( ( 0 + 1 - 4 ) + 1 ) + 1 + 4 ,
( ( 0 + 1 - 4 ) + 1 ) + 1 + 4 } ,
3 ,
vals . Validators [ 0 ] } ,
3 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 4 * ( 10 - 12 ) + 4 - 4 , // still mostest
0 + 4 * 1 + 4 - 4 ,
0 + 4 * 1 + 4 - 4 } ,
4 ,
vals . Validators [ 0 ] } ,
4 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 4 * ( 10 - 12 ) + 10 + 4 - 4 , // 4 iters was mostest
0 + 5 * 1 - 12 + 4 - 4 , // now this val is mostest for the 1st time (hence -12==totalVotingPower)
0 + 5 * 1 + 4 - 4 } ,
5 ,
vals . Validators [ 1 ] } ,
5 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 6 * 10 - 5 * 12 + 4 - 4 , // mostest again
0 + 6 * 1 - 12 + 4 - 4 , // mostest once up to here
0 + 6 * 1 + 4 - 4 } ,
6 ,
vals . Validators [ 0 ] } ,
6 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 7 * 10 - 6 * 12 + 4 - 4 , // in 7 iters this val is mostest 6 times
0 + 7 * 1 - 12 + 4 - 4 , // in 7 iters this val is mostest 1 time
0 + 7 * 1 + 4 - 4 } ,
7 ,
vals . Validators [ 0 ] } ,
7 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 8 * 10 - 7 * 12 + 4 - 4 , // mostest
0 + 8 * 1 - 12 + 4 - 4 ,
0 + 8 * 1 + 4 - 4 } ,
8 ,
vals . Validators [ 0 ] } ,
8 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 9 * 10 - 7 * 12 + 4 - 4 ,
0 + 9 * 1 - 12 + 4 - 4 ,
0 + 9 * 1 - 12 + 4 - 4 } , // mostest
9 ,
vals . Validators [ 2 ] } ,
9 : {
vals . Copy ( ) ,
[ ] int64 {
0 + 10 * 10 - 8 * 12 + 4 - 4 , // after 10 iters this is mostest again
0 + 10 * 1 - 12 + 4 - 4 , // after 6 iters this val is "mostest" once and not in between
0 + 10 * 1 - 12 + 4 - 4 } , // in between 10 iters this val is "mostest" once
10 ,
vals . Validators [ 0 ] } ,
10 : {
vals . Copy ( ) ,
[ ] int64 {
// shift twice inside incrementProposerPriority (shift every 10th iter);
// don't shift at the end of IncremenctProposerPriority
// last avg should be zero because
// ProposerPriority of validator 0: (0 + 11*10 - 8*12 - 4) == 10
// ProposerPriority of validator 1 and 2: (0 + 11*1 - 12 - 4) == -5
// and (10 + 5 - 5) / 3 == 0
0 + 11 * 10 - 8 * 12 - 4 - 12 - 0 ,
0 + 11 * 1 - 12 - 4 - 0 , // after 6 iters this val is "mostest" once and not in between
0 + 11 * 1 - 12 - 4 - 0 } , // after 10 iters this val is "mostest" once
11 ,
vals . Validators [ 0 ] } ,
}
for i , tc := range tcs {
tc . vals . IncrementProposerPriority ( tc . times )
assert . Equal ( t , tc . wantProposer . Address , tc . vals . GetProposer ( ) . Address ,
"test case: %v" ,
i )
for valIdx , val := range tc . vals . Validators {
assert . Equal ( t ,
tc . wantProposerPrioritys [ valIdx ] ,
val . ProposerPriority ,
"test case: %v, validator: %v" ,
i ,
valIdx )
}
}
}
func TestSafeAdd ( t * testing . T ) {