Skip to content

Commit

Permalink
Extracted ValidatorSetDelta tests into separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
dusan-maksimovic committed May 15, 2023
1 parent d0b5a13 commit 7c6def7
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 281 deletions.
226 changes: 0 additions & 226 deletions consensus/polybft/extra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,93 +487,6 @@ func TestSignature_VerifyRandom(t *testing.T) {
assert.NoError(t, err)
}

func TestExtra_CreateValidatorSetDelta_Cases(t *testing.T) {
t.Parallel()

cases := []struct {
name string
oldSet []string
newSet []string
added []string
updated []string
removed []uint64
}{
{
"Simple",
[]string{"A", "B", "C", "E", "F"},
[]string{"B", "E", "H"},
[]string{"H"},
[]string{"B", "E"},
[]uint64{0, 2, 4},
},
}

for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()

vals := validator.NewTestValidatorsWithAliases(t, []string{})

for _, name := range c.oldSet {
vals.Create(t, name, 1)
}
for _, name := range c.newSet {
vals.Create(t, name, 1)
}

oldValidatorSet := vals.GetPublicIdentities(c.oldSet...)
// update voting power to random value
maxVotingPower := big.NewInt(100)
for _, name := range c.updated {
v := vals.GetValidator(name)
vp, err := rand.Int(rand.Reader, maxVotingPower)
require.NoError(t, err)
// make sure generated voting power is different than the original one
v.VotingPower += vp.Uint64() + 1
}
newValidatorSet := vals.GetPublicIdentities(c.newSet...)

delta, err := createValidatorSetDelta(oldValidatorSet, newValidatorSet)
require.NoError(t, err)

// added validators
require.Len(t, delta.Added, len(c.added))
for i, name := range c.added {
require.Equal(t, delta.Added[i].Address, vals.GetValidator(name).Address())
}

// removed validators
for _, i := range c.removed {
require.True(t, delta.Removed.IsSet(i))
}

// updated validators
require.Len(t, delta.Updated, len(c.updated))
for i, name := range c.updated {
require.Equal(t, delta.Updated[i].Address, vals.GetValidator(name).Address())
}
})
}
}

func TestExtra_CreateValidatorSetDelta_BlsDiffer(t *testing.T) {
t.Parallel()

vals := validator.NewTestValidatorsWithAliases(t, []string{"A", "B", "C", "D", "E", "F"})
oldValidatorSet := vals.GetPublicIdentities("A", "B", "C", "D")

// change the public bls key of 'B'
newValidatorSet := vals.GetPublicIdentities("B", "E", "F")
privateKey, err := bls.GenerateBlsKey()
require.NoError(t, err)

newValidatorSet[0].BlsKey = privateKey.PublicKey()

_, err = createValidatorSetDelta(oldValidatorSet, newValidatorSet)
require.Error(t, err)
}

func TestExtra_InitGenesisValidatorsDelta(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -634,145 +547,6 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) {
})
}

func TestValidatorSetDelta_Copy(t *testing.T) {
t.Parallel()

const (
originalValidatorsCount = 10
addedValidatorsCount = 2
)

oldValidatorSet := validator.NewTestValidators(t, originalValidatorsCount).GetPublicIdentities()
newValidatorSet := oldValidatorSet[:len(oldValidatorSet)-2]
originalDelta, err := createValidatorSetDelta(oldValidatorSet, newValidatorSet)
require.NoError(t, err)
require.NotNil(t, originalDelta)
require.Empty(t, originalDelta.Added)

copiedDelta := originalDelta.Copy()
require.NotNil(t, copiedDelta)
require.NotSame(t, originalDelta, copiedDelta)
require.NotEqual(t, originalDelta, copiedDelta)
require.Empty(t, copiedDelta.Added)
require.Equal(t, copiedDelta.Removed.Len(), originalDelta.Removed.Len())

newValidators := validator.NewTestValidators(t, addedValidatorsCount).GetPublicIdentities()
copiedDelta.Added = append(copiedDelta.Added, newValidators...)
require.Empty(t, originalDelta.Added)
require.Len(t, copiedDelta.Added, addedValidatorsCount)
require.Equal(t, copiedDelta.Removed.Len(), originalDelta.Removed.Len())
}

func TestValidatorSetDelta_UnmarshalRLPWith_NegativeCases(t *testing.T) {
t.Parallel()

t.Run("Incorrect RLP value type provided", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(ar.NewNull()), "value is not of type array")
})

t.Run("Empty RLP array provided", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
delta := &validator.ValidatorSetDelta{}
require.NoError(t, delta.UnmarshalRLPWith(ar.NewArray()))
})

t.Run("Incorrect RLP array size", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
deltaMarshalled.Set(ar.NewBytes([]byte{0x59}))
deltaMarshalled.Set(ar.NewBytes([]byte{0x33}))
deltaMarshalled.Set(ar.NewBytes([]byte{0x26}))
deltaMarshalled.Set(ar.NewBytes([]byte{0x74}))
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "incorrect elements count to decode validator set delta, expected 3 but found 4")
})

t.Run("Incorrect RLP value type for Added field", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
deltaMarshalled.Set(ar.NewBytes([]byte{0x59}))
deltaMarshalled.Set(ar.NewBytes([]byte{0x33}))
deltaMarshalled.Set(ar.NewBytes([]byte{0x27}))
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "array expected for added validators")
})

t.Run("Incorrect RLP value type for ValidatorMetadata in Added field", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
addedArray := ar.NewArray()
addedArray.Set(ar.NewNull())
deltaMarshalled.Set(addedArray)
deltaMarshalled.Set(ar.NewNullArray())
deltaMarshalled.Set(ar.NewNull())
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "value is not of type array")
})

t.Run("Incorrect RLP value type for Removed field", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
addedValidators := validator.NewTestValidators(t, 3).GetPublicIdentities()
addedArray := ar.NewArray()
updatedArray := ar.NewArray()
for _, validator := range addedValidators {
addedArray.Set(validator.MarshalRLPWith(ar))
}
for _, validator := range addedValidators {
votingPower, err := rand.Int(rand.Reader, big.NewInt(100))
require.NoError(t, err)

validator.VotingPower = new(big.Int).Set(votingPower)
updatedArray.Set(validator.MarshalRLPWith(ar))
}
deltaMarshalled.Set(addedArray)
deltaMarshalled.Set(updatedArray)
deltaMarshalled.Set(ar.NewNull())
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "value is not of type bytes")
})

t.Run("Incorrect RLP value type for Updated field", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
deltaMarshalled.Set(ar.NewArray())
deltaMarshalled.Set(ar.NewBytes([]byte{0x33}))
deltaMarshalled.Set(ar.NewNull())
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "array expected for updated validators")
})

t.Run("Incorrect RLP value type for ValidatorMetadata in Updated field", func(t *testing.T) {
t.Parallel()

ar := &fastrlp.Arena{}
deltaMarshalled := ar.NewArray()
updatedArray := ar.NewArray()
updatedArray.Set(ar.NewNull())
deltaMarshalled.Set(ar.NewArray())
deltaMarshalled.Set(updatedArray)
deltaMarshalled.Set(ar.NewNull())
delta := &validator.ValidatorSetDelta{}
require.ErrorContains(t, delta.UnmarshalRLPWith(deltaMarshalled), "value is not of type array")
})
}

func Test_GetIbftExtraClean(t *testing.T) {
t.Parallel()

Expand Down
49 changes: 0 additions & 49 deletions consensus/polybft/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,52 +175,3 @@ func generateTestAccount(t *testing.T) *wallet.Account {

return acc
}

// createValidatorSetDelta calculates ValidatorSetDelta based on the provided old and new validator sets
func createValidatorSetDelta(oldValidatorSet, newValidatorSet validator.AccountSet) (*validator.ValidatorSetDelta, error) {
var addedValidators, updatedValidators validator.AccountSet

oldValidatorSetMap := make(map[types.Address]*validator.ValidatorMetadata)
removedValidators := map[types.Address]int{}

for i, validator := range oldValidatorSet {
if (validator.Address != types.Address{}) {
removedValidators[validator.Address] = i
oldValidatorSetMap[validator.Address] = validator
}
}

for _, newValidator := range newValidatorSet {
// Check if the validator is among both old and new validator set
oldValidator, validatorExists := oldValidatorSetMap[newValidator.Address]
if validatorExists {
if !oldValidator.EqualAddressAndBlsKey(newValidator) {
return nil, fmt.Errorf("validator '%s' found in both old and new validator set, but its BLS keys differ",
newValidator.Address.String())
}

// If it is, then discard it from removed validators...
delete(removedValidators, newValidator.Address)

if !oldValidator.Equals(newValidator) {
updatedValidators = append(updatedValidators, newValidator)
}
} else {
// ...otherwise it is added
addedValidators = append(addedValidators, newValidator)
}
}

removedValsBitmap := bitmap.Bitmap{}
for _, i := range removedValidators {
removedValsBitmap.Set(uint64(i))
}

delta := &validator.ValidatorSetDelta{
Added: addedValidators,
Updated: updatedValidators,
Removed: removedValsBitmap,
}

return delta, nil
}
1 change: 0 additions & 1 deletion consensus/polybft/mocks_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package polybft

import (
"math/big"
"time"

"github.com/0xPolygon/polygon-edge/blockchain"
Expand Down
8 changes: 4 additions & 4 deletions consensus/polybft/polybft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestPolybft_VerifyHeader(t *testing.T) {
headersMap := &testHeadersMap{}

// create genesis header
genesisDelta, err := createValidatorSetDelta(nil, validatorSetParent)
genesisDelta, err := validator.CreateValidatorSetDelta(nil, validatorSetParent)
require.NoError(t, err)

genesisHeader := &types.Header{Number: 0}
Expand All @@ -93,7 +93,7 @@ func TestPolybft_VerifyHeader(t *testing.T) {

// create headers from 1 to 9
for i := uint64(1); i < polyBftConfig.EpochSize; i++ {
delta, err := createValidatorSetDelta(validatorSetParent, validatorSetParent)
delta, err := validator.CreateValidatorSetDelta(validatorSetParent, validatorSetParent)
require.NoError(t, err)

header := &types.Header{Number: i}
Expand Down Expand Up @@ -122,7 +122,7 @@ func TestPolybft_VerifyHeader(t *testing.T) {
}

// create parent header (block 10)
parentDelta, err := createValidatorSetDelta(validatorSetParent, validatorSetCurrent)
parentDelta, err := validator.CreateValidatorSetDelta(validatorSetParent, validatorSetCurrent)
require.NoError(t, err)

parentHeader := &types.Header{
Expand All @@ -135,7 +135,7 @@ func TestPolybft_VerifyHeader(t *testing.T) {
headersMap.addHeader(parentHeader)

// create current header (block 11) with all appropriate fields required for validation
currentDelta, err := createValidatorSetDelta(validatorSetCurrent, validatorSetCurrent)
currentDelta, err := validator.CreateValidatorSetDelta(validatorSetCurrent, validatorSetCurrent)
require.NoError(t, err)

currentHeader := &types.Header{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"testing"

"github.com/0xPolygon/polygon-edge/consensus/polybft/bitmap"
bls "github.com/0xPolygon/polygon-edge/consensus/polybft/signer"
"github.com/0xPolygon/polygon-edge/consensus/polybft/wallet"
"github.com/0xPolygon/polygon-edge/types"
Expand Down Expand Up @@ -192,3 +193,52 @@ func generateTestAccount(t *testing.T) *wallet.Account {

return acc
}

// CreateValidatorSetDelta calculates ValidatorSetDelta based on the provided old and new validator sets
func CreateValidatorSetDelta(oldValidatorSet, newValidatorSet AccountSet) (*ValidatorSetDelta, error) {
var addedValidators, updatedValidators AccountSet

oldValidatorSetMap := make(map[types.Address]*ValidatorMetadata)
removedValidators := map[types.Address]int{}

for i, validator := range oldValidatorSet {
if (validator.Address != types.Address{}) {
removedValidators[validator.Address] = i
oldValidatorSetMap[validator.Address] = validator
}
}

for _, newValidator := range newValidatorSet {
// Check if the validator is among both old and new validator set
oldValidator, validatorExists := oldValidatorSetMap[newValidator.Address]
if validatorExists {
if !oldValidator.EqualAddressAndBlsKey(newValidator) {
return nil, fmt.Errorf("validator '%s' found in both old and new validator set, but its BLS keys differ",
newValidator.Address.String())
}

// If it is, then discard it from removed validators...
delete(removedValidators, newValidator.Address)

if !oldValidator.Equals(newValidator) {
updatedValidators = append(updatedValidators, newValidator)
}
} else {
// ...otherwise it is added
addedValidators = append(addedValidators, newValidator)
}
}

removedValsBitmap := bitmap.Bitmap{}
for _, i := range removedValidators {
removedValsBitmap.Set(uint64(i))
}

delta := &ValidatorSetDelta{
Added: addedValidators,
Updated: updatedValidators,
Removed: removedValsBitmap,
}

return delta, nil
}
Loading

0 comments on commit 7c6def7

Please sign in to comment.