From 44fa8e9492b90519501e265abd0a0ed6f70039ce Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 5 Jun 2023 20:35:05 +0200 Subject: [PATCH 01/27] fix --- go.mod | 1 + go.sum | 2 + vms/platformvm/validators/manager.go | 134 ++- vms/platformvm/validators/manager_test.go | 300 +++--- .../validators_set_property_test.go | 853 ++++++++++++++++++ vms/platformvm/vm_regression_test.go | 761 ++++++++++++++++ 6 files changed, 1857 insertions(+), 194 deletions(-) create mode 100644 vms/platformvm/validators_set_property_test.go diff --git a/go.mod b/go.mod index 413347fdb575..a9af1d80e3af 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/huin/goupnp v1.0.3 github.com/jackpal/gateway v1.0.6 github.com/jackpal/go-nat-pmp v1.0.2 + github.com/leanovate/gopter v0.2.9 github.com/mr-tron/base58 v1.2.0 github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d github.com/onsi/ginkgo/v2 v2.4.0 diff --git a/go.sum b/go.sum index 836fb8c766af..a3005081e397 100644 --- a/go.sum +++ b/go.sum @@ -313,6 +313,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 7ded28c7c4e5..35d48aa42243 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -164,100 +164,138 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return nil, err } } + currentSubnetValidatorList := currentSubnetValidators.List() + subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) + for _, vdr := range currentSubnetValidatorList { + subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ + NodeID: vdr.NodeID, + // PublicKey will be picked from primary validators + Weight: vdr.Weight, + } + } + currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { // This should never happen return nil, ErrMissingValidator } - - currentSubnetValidatorList := currentSubnetValidators.List() - vdrSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) - for _, vdr := range currentSubnetValidatorList { - primaryVdr, ok := currentPrimaryNetworkValidators.Get(vdr.NodeID) - if !ok { - // This should never happen - return nil, fmt.Errorf("%w: %s", ErrMissingValidator, vdr.NodeID) - } - vdrSet[vdr.NodeID] = &validators.GetValidatorOutput{ + currentPrimaryValidatorList := currentPrimaryNetworkValidators.List() + primarySet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentPrimaryValidatorList)) + for _, vdr := range currentPrimaryValidatorList { + primarySet[vdr.NodeID] = &validators.GetValidatorOutput{ NodeID: vdr.NodeID, - PublicKey: primaryVdr.PublicKey, + PublicKey: vdr.PublicKey, Weight: vdr.Weight, } + + // fill PK from primary network + if _, found := subnetSet[vdr.NodeID]; found { + subnetSet[vdr.NodeID].PublicKey = vdr.PublicKey + } } for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { - err := m.applyValidatorDiffs(vdrSet, subnetID, diffHeight) + err := m.applyValidatorDiffs(subnetSet, primarySet, subnetID, diffHeight) if err != nil { return nil, err } } // cache the validator set - validatorSetsCache.Put(height, vdrSet) + validatorSetsCache.Put(height, subnetSet) endTime := m.clk.Time() m.metrics.IncValidatorSetsCreated() m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) m.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) - return vdrSet, nil + return subnetSet, nil } func (m *manager) applyValidatorDiffs( - vdrSet map[ids.NodeID]*validators.GetValidatorOutput, + targetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, subnetID ids.ID, height uint64, ) error { - weightDiffs, err := m.state.GetValidatorWeightDiffs(height, subnetID) + // fully rebuild primary network validators at [height] + primaryWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, constants.PlatformChainID) if err != nil { return err } - - for nodeID, weightDiff := range weightDiffs { - vdr, ok := vdrSet[nodeID] - if !ok { - // This node isn't in the current validator set. - vdr = &validators.GetValidatorOutput{ - NodeID: nodeID, - } - vdrSet[nodeID] = vdr - } - - // The weight of this node changed at this block. - if weightDiff.Decrease { - // The validator's weight was decreased at this block, so in the - // prior block it was higher. - vdr.Weight, err = math.Add64(vdr.Weight, weightDiff.Amount) - } else { - // The validator's weight was increased at this block, so in the - // prior block it was lower. - vdr.Weight, err = math.Sub(vdr.Weight, weightDiff.Amount) - } + for nodeID, weightDiff := range primaryWeightDiffs { + err := rebuildWeight(primarySet, nodeID, weightDiff) if err != nil { return err } - - if vdr.Weight == 0 { - // The validator's weight was 0 before this block so - // they weren't in the validator set. - delete(vdrSet, nodeID) - } } pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(height) if err != nil { return err } - for nodeID, pk := range pkDiffs { - // pkDiffs includes all primary network key diffs, if we are - // fetching a subnet's validator set, we should ignore non-subnet - // validators. - if vdr, ok := vdrSet[nodeID]; ok { + if vdr, ok := primarySet[nodeID]; ok { // The validator's public key was removed at this block, so it // was in the validator set before. vdr.PublicKey = pk } } + + // rebuild weights of target validators + targetWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, subnetID) + if err != nil { + return err + } + for nodeID, weightDiff := range targetWeightDiffs { + err := rebuildWeight(targetSet, nodeID, weightDiff) + if err != nil { + return err + } + } + + // rebuild public key of target validators, just peeking in primary validators set + for nodeID, vdr := range targetSet { + if primary, found := primarySet[nodeID]; found { + vdr.PublicKey = primary.PublicKey + } + } + + return nil +} + +func rebuildWeight( + targetSet map[ids.NodeID]*validators.GetValidatorOutput, + nodeID ids.NodeID, + weightDiff *state.ValidatorWeightDiff, +) error { + vdr, ok := targetSet[nodeID] + if !ok { + // This node isn't in the current validator set. + vdr = &validators.GetValidatorOutput{ + NodeID: nodeID, + } + targetSet[nodeID] = vdr + } + + // The weight of this node changed at this block. + var err error + if weightDiff.Decrease { + // The validator's weight was decreased at this block, so in the + // prior block it was higher. + vdr.Weight, err = math.Add64(vdr.Weight, weightDiff.Amount) + } else { + // The validator's weight was increased at this block, so in the + // prior block it was lower. + vdr.Weight, err = math.Sub(vdr.Weight, weightDiff.Amount) + } + if err != nil { + return err + } + + if vdr.Weight == 0 { + // The validator's weight was 0 before this block so + // they weren't in the validator set. + delete(targetSet, nodeID) + } return nil } diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 7f5d8975f427..43539771775a 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/golang/mock/gomock" + "golang.org/x/exp/maps" "github.com/prometheus/client_golang/prometheus" @@ -31,7 +32,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/state" ) -// AVAX asset ID in tests var defaultRewardConfig = reward.Config{ MaxConsumptionRate: .12 * reward.PercentDenominator, MinConsumptionRate: .10 * reward.PercentDenominator, @@ -42,16 +42,16 @@ var defaultRewardConfig = reward.Config{ func TestVM_GetValidatorSet(t *testing.T) { // Populate the validator set to use below var ( - numVdrs = 4 - vdrBaseWeight = uint64(1_000) - vdrs []*validators.Validator + numVdrs = 4 + vdrBaseWeight = uint64(1_000) + testValidators []*validators.Validator ) for i := 0; i < numVdrs; i++ { sk, err := bls.NewSecretKey() require.NoError(t, err) - vdrs = append(vdrs, &validators.Validator{ + testValidators = append(testValidators, &validators.Validator{ NodeID: ids.GenerateTestNodeID(), PublicKey: bls.PublicFromSecretKey(sk), Weight: vdrBaseWeight + uint64(i), @@ -65,14 +65,14 @@ func TestVM_GetValidatorSet(t *testing.T) { lastAcceptedHeight uint64 subnetID ids.ID // Validator sets at tip - currentPrimaryNetworkValidators []*validators.Validator + currentPrimaryNetworkValidators map[ids.NodeID]*validators.Validator currentSubnetValidators []*validators.Validator - // Diff at tip, block before tip, etc. - // This must have [lastAcceptedHeight] - [height] elements - weightDiffs []map[ids.NodeID]*state.ValidatorWeightDiff - // Diff at tip, block before tip, etc. - // This must have [lastAcceptedHeight] - [height] elements - pkDiffs []map[ids.NodeID]*bls.PublicKey + + // height --> nodeID --> weightDiff + weightDiffs map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff + + // height --> nodeID --> pkDiff + pkDiffs map[uint64]map[ids.NodeID]*bls.PublicKey expectedVdrSet map[ids.NodeID]*validators.GetValidatorOutput expectedErr error } @@ -87,19 +87,20 @@ func TestVM_GetValidatorSet(t *testing.T) { }, { name: "at tip", + subnetID: constants.PrimaryNetworkID, height: 1, lastAcceptedHeight: 1, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), }, currentSubnetValidators: []*validators.Validator{ - copySubnetValidator(vdrs[0]), + copySubnetValidator(testValidators[0]), }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight, }, }, expectedErr: nil, @@ -108,53 +109,53 @@ func TestVM_GetValidatorSet(t *testing.T) { name: "1 before tip", height: 2, lastAcceptedHeight: 3, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), + testValidators[1].NodeID: copyPrimaryValidator(testValidators[1]), }, currentSubnetValidators: []*validators.Validator{ // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), + copySubnetValidator(testValidators[0]), + copySubnetValidator(testValidators[1]), }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { + weightDiffs: map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff{ + 3: { // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, // and vdrs[2] left - vdrs[0].NodeID: { + testValidators[0].NodeID: { Decrease: true, Amount: 1, }, - vdrs[1].NodeID: { + testValidators[1].NodeID: { Decrease: false, Amount: 1, }, - vdrs[2].NodeID: { + testValidators[2].NodeID: { Decrease: true, - Amount: vdrs[2].Weight, + Amount: testValidators[2].Weight, }, }, }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[2].NodeID: vdrs[2].PublicKey, + pkDiffs: map[uint64]map[ids.NodeID]*bls.PublicKey{ + 3: { + testValidators[2].NodeID: testValidators[2].PublicKey, }, }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight + 1, }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, + testValidators[1].NodeID: { + NodeID: testValidators[1].NodeID, + PublicKey: testValidators[1].PublicKey, + Weight: testValidators[1].Weight - 1, }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - PublicKey: vdrs[2].PublicKey, - Weight: vdrs[2].Weight, + testValidators[2].NodeID: { + NodeID: testValidators[2].NodeID, + PublicKey: testValidators[2].PublicKey, + Weight: testValidators[2].Weight, }, }, expectedErr: nil, @@ -163,65 +164,65 @@ func TestVM_GetValidatorSet(t *testing.T) { name: "2 before tip", height: 3, lastAcceptedHeight: 5, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), + testValidators[1].NodeID: copyPrimaryValidator(testValidators[1]), }, currentSubnetValidators: []*validators.Validator{ // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), + copySubnetValidator(testValidators[0]), + copySubnetValidator(testValidators[1]), }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { + weightDiffs: map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff{ + 5: { // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, // and vdrs[2] left - vdrs[0].NodeID: { + testValidators[0].NodeID: { Decrease: true, Amount: 1, }, - vdrs[1].NodeID: { + testValidators[1].NodeID: { Decrease: false, Amount: 1, }, - vdrs[2].NodeID: { + testValidators[2].NodeID: { Decrease: true, - Amount: vdrs[2].Weight, + Amount: testValidators[2].Weight, }, }, - { + 4: { // At the block before tip vdrs[0] lost weight, vdrs[1] gained weight, // vdrs[2] joined - vdrs[0].NodeID: { + testValidators[0].NodeID: { Decrease: true, Amount: 1, }, - vdrs[1].NodeID: { + testValidators[1].NodeID: { Decrease: false, Amount: 1, }, - vdrs[2].NodeID: { + testValidators[2].NodeID: { Decrease: false, - Amount: vdrs[2].Weight, + Amount: testValidators[2].Weight, }, }, }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[2].NodeID: vdrs[2].PublicKey, + pkDiffs: map[uint64]map[ids.NodeID]*bls.PublicKey{ + 5: { + testValidators[2].NodeID: testValidators[2].PublicKey, }, - {}, + 4: {}, }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 2, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight + 2, }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 2, + testValidators[1].NodeID: { + NodeID: testValidators[1].NodeID, + PublicKey: testValidators[1].PublicKey, + Weight: testValidators[1].Weight - 2, }, }, expectedErr: nil, @@ -230,50 +231,50 @@ func TestVM_GetValidatorSet(t *testing.T) { name: "1 before tip; nil public key", height: 4, lastAcceptedHeight: 5, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), + testValidators[1].NodeID: copyPrimaryValidator(testValidators[1]), }, currentSubnetValidators: []*validators.Validator{ // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), + copySubnetValidator(testValidators[0]), + copySubnetValidator(testValidators[1]), }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { + weightDiffs: map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff{ + 5: { // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, // and vdrs[2] left - vdrs[0].NodeID: { + testValidators[0].NodeID: { Decrease: true, Amount: 1, }, - vdrs[1].NodeID: { + testValidators[1].NodeID: { Decrease: false, Amount: 1, }, - vdrs[2].NodeID: { + testValidators[2].NodeID: { Decrease: true, - Amount: vdrs[2].Weight, + Amount: testValidators[2].Weight, }, }, }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - {}, + pkDiffs: map[uint64]map[ids.NodeID]*bls.PublicKey{ + 5: {}, }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight + 1, }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, + testValidators[1].NodeID: { + NodeID: testValidators[1].NodeID, + PublicKey: testValidators[1].PublicKey, + Weight: testValidators[1].Weight - 1, }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - Weight: vdrs[2].Weight, + testValidators[2].NodeID: { + NodeID: testValidators[2].NodeID, + Weight: testValidators[2].Weight, }, }, expectedErr: nil, @@ -283,51 +284,51 @@ func TestVM_GetValidatorSet(t *testing.T) { height: 5, lastAcceptedHeight: 6, subnetID: ids.GenerateTestID(), - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), - copyPrimaryValidator(vdrs[3]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), + testValidators[1].NodeID: copyPrimaryValidator(testValidators[1]), + testValidators[3].NodeID: copyPrimaryValidator(testValidators[3]), }, currentSubnetValidators: []*validators.Validator{ // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), + copySubnetValidator(testValidators[0]), + copySubnetValidator(testValidators[1]), }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { + weightDiffs: map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff{ + 6: { // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, // and vdrs[2] left - vdrs[0].NodeID: { + testValidators[0].NodeID: { Decrease: true, Amount: 1, }, - vdrs[1].NodeID: { + testValidators[1].NodeID: { Decrease: false, Amount: 1, }, - vdrs[2].NodeID: { + testValidators[2].NodeID: { Decrease: true, - Amount: vdrs[2].Weight, + Amount: testValidators[2].Weight, }, }, }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - {}, + pkDiffs: map[uint64]map[ids.NodeID]*bls.PublicKey{ + 6: {}, }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight + 1, }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, + testValidators[1].NodeID: { + NodeID: testValidators[1].NodeID, + PublicKey: testValidators[1].PublicKey, + Weight: testValidators[1].Weight - 1, }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - Weight: vdrs[2].Weight, + testValidators[2].NodeID: { + NodeID: testValidators[2].NodeID, + Weight: testValidators[2].Weight, }, }, expectedErr: nil, @@ -337,25 +338,25 @@ func TestVM_GetValidatorSet(t *testing.T) { height: 4, lastAcceptedHeight: 5, subnetID: ids.GenerateTestID(), - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), + currentPrimaryNetworkValidators: map[ids.NodeID]*validators.Validator{ + testValidators[0].NodeID: copyPrimaryValidator(testValidators[0]), }, currentSubnetValidators: []*validators.Validator{ - copySubnetValidator(vdrs[0]), + copySubnetValidator(testValidators[0]), }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - {}, + weightDiffs: map[uint64]map[ids.NodeID]*state.ValidatorWeightDiff{ + 5: {}, }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[1].NodeID: vdrs[1].PublicKey, + pkDiffs: map[uint64]map[ids.NodeID]*bls.PublicKey{ + 5: { + testValidators[1].NodeID: testValidators[1].PublicKey, }, }, expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight, + testValidators[0].NodeID: { + NodeID: testValidators[0].NodeID, + PublicKey: testValidators[0].PublicKey, + Weight: testValidators[0].Weight, }, }, expectedErr: nil, @@ -387,26 +388,33 @@ func TestVM_GetValidatorSet(t *testing.T) { validatorssSet := NewManager(cfg, mockState, metrics, clk) // Mock the VM's validators - mockSubnetVdrSet := validators.NewMockSet(ctrl) - mockSubnetVdrSet.EXPECT().List().Return(tt.currentSubnetValidators).AnyTimes() - vdrs.EXPECT().Get(tt.subnetID).Return(mockSubnetVdrSet, true).AnyTimes() + mockPrimaryVdrSet := validators.NewMockSet(ctrl) + mockPrimaryVdrSet.EXPECT().List().Return(maps.Values(tt.currentPrimaryNetworkValidators)).AnyTimes() + vdrs.EXPECT().Get(constants.PrimaryNetworkID).Return(mockPrimaryVdrSet, true).AnyTimes() - mockPrimaryVdrSet := mockSubnetVdrSet + mockSubnetVdrSet := mockPrimaryVdrSet if tt.subnetID != constants.PrimaryNetworkID { - mockPrimaryVdrSet = validators.NewMockSet(ctrl) - vdrs.EXPECT().Get(constants.PrimaryNetworkID).Return(mockPrimaryVdrSet, true).AnyTimes() + mockSubnetVdrSet = validators.NewMockSet(ctrl) + mockSubnetVdrSet.EXPECT().List().Return(tt.currentSubnetValidators).AnyTimes() } - for _, vdr := range tt.currentPrimaryNetworkValidators { - mockPrimaryVdrSet.EXPECT().Get(vdr.NodeID).Return(vdr, true).AnyTimes() + vdrs.EXPECT().Get(tt.subnetID).Return(mockSubnetVdrSet, true).AnyTimes() + + for _, vdr := range testValidators { + _, current := tt.currentPrimaryNetworkValidators[vdr.NodeID] + if current { + mockPrimaryVdrSet.EXPECT().Get(vdr.NodeID).Return(vdr, true).AnyTimes() + } else { + mockPrimaryVdrSet.EXPECT().Get(vdr.NodeID).Return(nil, false).AnyTimes() + } } // Tell state what diffs to report - for _, weightDiff := range tt.weightDiffs { - mockState.EXPECT().GetValidatorWeightDiffs(gomock.Any(), gomock.Any()).Return(weightDiff, nil) + for height, weightDiff := range tt.weightDiffs { + mockState.EXPECT().GetValidatorWeightDiffs(height, gomock.Any()).Return(weightDiff, nil).AnyTimes() } - for _, pkDiff := range tt.pkDiffs { - mockState.EXPECT().GetValidatorPublicKeyDiffs(gomock.Any()).Return(pkDiff, nil) + for height, pkDiff := range tt.pkDiffs { + mockState.EXPECT().GetValidatorPublicKeyDiffs(height).Return(pkDiff, nil) } // Tell state last accepted block to report diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go new file mode 100644 index 000000000000..d96f0f35541a --- /dev/null +++ b/vms/platformvm/validators_set_property_test.go @@ -0,0 +1,853 @@ +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package platformvm + +import ( + "context" + "errors" + "fmt" + "reflect" + "testing" + "time" + + "github.com/ava-labs/avalanchego/chains" + "github.com/ava-labs/avalanchego/chains/atomic" + "github.com/ava-labs/avalanchego/database/manager" + "github.com/ava-labs/avalanchego/database/prefixdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/snow/consensus/snowman" + "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/uptime" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/formatting" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/utils/json" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/version" + "github.com/ava-labs/avalanchego/vms/avm/blocks/executor" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/api" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/utxo" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" + + blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" +) + +const ( + startPrimaryWithBLS = "primary-validator-with-BLS-key\n" + startPrimaryWithoutBLS = "primary-validator-without-BLS-key\n" + startSubnetValidator = "subnet-validator\n" +) + +var errZeroLenghtEventsList = errors.New("unexpected zero length for events") + +func TestGetValidatorsSetProperty(t *testing.T) { + properties := gopter.NewProperties(nil) + + // to reproduce a given scenario do something like this: + // parameters := gopter.DefaultTestParametersWithSeed(1685887576153675816) + // properties := gopter.NewProperties(parameters) + + properties.Property("check GetValidatorSet", prop.ForAll( + func(events []string) string { + vm, subnetID, err := buildVM() + if err != nil { + return fmt.Sprintf("failed building vm, %s", err.Error()) + } + vm.ctx.Lock.Lock() + defer func() { + _ = vm.Shutdown(context.Background()) + vm.ctx.Lock.Unlock() + }() + nodeID := ids.GenerateTestNodeID() + + currentTime := defaultGenesisTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + // build validator sequence out of random (tey reproducible) input + validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) + if err != nil { + return fmt.Sprintf("failed building events sequence, %s", err.Error()) + } + + validatorsSetByHeightAndSubnet := make(map[uint64]map[ids.ID]map[ids.NodeID]*validators.GetValidatorOutput) + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + + // insert validator sequence + var ( + currentPrimaryValidator = (*state.Staker)(nil) + currentSubnetValidator = (*state.Staker)(nil) + ) + for _, ev := range validatorsTimes { + // at each we remove at least a subnet validator + if currentSubnetValidator != nil { + err := terminateSubnetValidator(vm, currentSubnetValidator) + if err != nil { + return fmt.Sprintf("could not terminate current subnet validator, %s", err.Error()) + } + currentSubnetValidator = nil + + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + } + + switch ev.Priority { + case txs.SubnetPermissionlessValidatorCurrentPriority: + currentSubnetValidator, err = addSubnetValidator(vm, ev, subnetID) + if err != nil { + return fmt.Sprintf("could not add subnet validator, %s", err.Error()) + } + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + + case txs.PrimaryNetworkValidatorCurrentPriority: + // when adding a primary validator, also remove the current primary one + if currentPrimaryValidator != nil { + err := terminatePrimaryValidator(vm, currentPrimaryValidator) + if err != nil { + return fmt.Sprintf("could not terminate current primary validator, %s", err.Error()) + } + // no need to nil current primary validator, we'll reassign immediately + + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + } + + if ev.PublicKey == nil { + currentPrimaryValidator, err = addPrimaryValidatorWithoutBLSKey(vm, ev) + if err != nil { + return fmt.Sprintf("could not create AddValidatorTx, %s", err.Error()) + } + } else { + currentPrimaryValidator, err = addPrimaryValidatorWithBLSKey(vm, ev) + if err != nil { + return fmt.Sprintf("could not add primary validator with BLS key, %s", err.Error()) + } + } + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + + default: + return fmt.Sprintf("unexpected staker type, %v", ev.Priority) + } + } + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } + + // finally test validators set + for height, subnetSets := range validatorsSetByHeightAndSubnet { + for subnet, validatorsSet := range subnetSets { + res, err := vm.GetValidatorSet(context.Background(), height, subnet) + if err != nil { + return fmt.Sprintf("failed GetValidatorSet, %v", err) + } + if !reflect.DeepEqual(validatorsSet, res) { + return fmt.Sprintf("failed validators set comparison, %v", err) + } + } + } + return "" + }, + gen.SliceOfN(10, gen.OneConstOf( + startPrimaryWithBLS, + startPrimaryWithoutBLS, + startSubnetValidator, + )).SuchThat(func(v interface{}) bool { + list := v.([]string) + return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) + }), + ), + ) + + properties.TestingRun(t) +} + +func takeValidatorsSnapshotAtCurrentHeight(vm *VM, validatorsSetByHeightAndSubnet map[uint64]map[ids.ID]map[ids.NodeID]*validators.GetValidatorOutput) error { + if validatorsSetByHeightAndSubnet == nil { + validatorsSetByHeightAndSubnet = make(map[uint64]map[ids.ID]map[ids.NodeID]*validators.GetValidatorOutput) + } + + lastBlkID := vm.state.GetLastAccepted() + lastBlk, _, err := vm.state.GetStatelessBlock(lastBlkID) + if err != nil { + return err + } + height := lastBlk.Height() + validatorsSetBySubnet, ok := validatorsSetByHeightAndSubnet[height] + if !ok { + validatorsSetByHeightAndSubnet[height] = make(map[ids.ID]map[ids.NodeID]*validators.GetValidatorOutput) + validatorsSetBySubnet = validatorsSetByHeightAndSubnet[height] + } + + stakerIt, err := vm.state.GetCurrentStakerIterator() + if err != nil { + return err + } + for stakerIt.Next() { + v := *stakerIt.Value() + validatorsSet, ok := validatorsSetBySubnet[v.SubnetID] + if !ok { + validatorsSetBySubnet[v.SubnetID] = make(map[ids.NodeID]*validators.GetValidatorOutput) + validatorsSet = validatorsSetBySubnet[v.SubnetID] + } + + blsKey := v.PublicKey + if v.SubnetID != constants.PlatformChainID { + // pick bls key from primary validator + s, err := vm.state.GetCurrentValidator(constants.PlatformChainID, v.NodeID) + if err != nil { + return err + } + blsKey = s.PublicKey + } + + validatorsSet[v.NodeID] = &validators.GetValidatorOutput{ + NodeID: v.NodeID, + PublicKey: blsKey, + Weight: v.Weight, + } + } + return nil +} + +func addSubnetValidator(vm *VM, data *state.Staker, subnetID ids.ID) (*state.Staker, error) { + addr := keys[0].PublicKey().Address() + signedTx, err := vm.txBuilder.NewAddSubnetValidatorTx( + vm.Config.MinValidatorStake, + uint64(data.StartTime.Unix()), + uint64(data.EndTime.Unix()), + data.NodeID, + subnetID, + []*secp256k1.PrivateKey{keys[0], keys[1]}, + addr, + ) + if err != nil { + return nil, fmt.Errorf("could not create AddSubnetValidatorTx, %w", err) + } + return internalAddValidator(vm, signedTx) +} + +func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, error) { + addr := keys[0].PublicKey().Address() + utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) + ins, unstakedOuts, stakedOuts, signers, err := utxoHandler.Spend( + vm.state, + keys, + vm.MinValidatorStake, + vm.Config.AddPrimaryNetworkValidatorFee, + addr, // change Addresss + ) + if err != nil { + return nil, fmt.Errorf("could not create inputs/ourputs for permissionless validator, %w", err) + } + sk, err := bls.NewSecretKey() + if err != nil { + return nil, fmt.Errorf("could not create secret key, %w", err) + } + + uPrimaryTx := &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: unstakedOuts, + }}, + Validator: txs.Validator{ + NodeID: data.NodeID, + Start: uint64(data.StartTime.Unix()), + End: uint64(data.EndTime.Unix()), + Wght: vm.MinValidatorStake, + }, + Subnet: constants.PrimaryNetworkID, + Signer: signer.NewProofOfPossession(sk), + StakeOuts: stakedOuts, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegationShares: reward.PercentDenominator, + } + signedTx, err := txs.NewSigned(uPrimaryTx, txs.Codec, signers) + if err != nil { + return nil, fmt.Errorf("could not create AddPermissionlessValidatorTx with BLS key, %w", err) + } + if err := signedTx.SyntacticVerify(vm.ctx); err != nil { + return nil, fmt.Errorf("failed sintax verification of AddPermissionlessValidatorTx, %w", err) + } + return internalAddValidator(vm, signedTx) +} + +func addPrimaryValidatorWithoutBLSKey(vm *VM, data *state.Staker) (*state.Staker, error) { + addr := keys[0].PublicKey().Address() + signedTx, err := vm.txBuilder.NewAddValidatorTx( + vm.Config.MinValidatorStake, + uint64(data.StartTime.Unix()), + uint64(data.EndTime.Unix()), + data.NodeID, + addr, + reward.PercentDenominator, + []*secp256k1.PrivateKey{keys[0], keys[1]}, + addr, + ) + if err != nil { + return nil, fmt.Errorf("could not create AddValidatorTx, %w", err) + } + return internalAddValidator(vm, signedTx) +} + +func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { + stakerTx := signedTx.Unsigned.(txs.StakerTx) + if err := vm.Builder.AddUnverifiedTx(signedTx); err != nil { + return nil, fmt.Errorf("could not add tx to mempool, %s", err.Error()) + } + + blk, err := vm.Builder.BuildBlock(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed building block, %s", err.Error()) + } + if err := blk.Verify(context.Background()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + if err := blk.Accept(context.Background()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + + // move time ahead, promoting the validator to current + currentTime := stakerTx.StartTime() + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + if err != nil { + return nil, fmt.Errorf("failed building block, %s", err.Error()) + } + if err := blk.Verify(context.Background()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + if err := blk.Accept(context.Background()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { + return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + } + + return vm.state.GetCurrentValidator(stakerTx.SubnetID(), stakerTx.NodeID()) +} + +func terminateSubnetValidator(vm *VM, validator *state.Staker) error { + currentTime := validator.EndTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err := vm.Builder.BuildBlock(context.Background()) + if err != nil { + return fmt.Errorf("failed building block, %s", err.Error()) + } + if err := blk.Verify(context.Background()); err != nil { + return fmt.Errorf("failed verifying block, %s", err.Error()) + } + if err := blk.Accept(context.Background()); err != nil { + return fmt.Errorf("failed accepting block, %s", err.Error()) + } + if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { + return fmt.Errorf("failed verifying block, %s", err.Error()) + } + + // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + // require.ErrorIs(err, database.ErrNotFound) + return nil +} + +func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { + currentTime := validator.EndTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err := vm.Builder.BuildBlock(context.Background()) + if err != nil { + return fmt.Errorf("failed building block, %s", err.Error()) + } + if err := blk.Verify(context.Background()); err != nil { + return fmt.Errorf("failed verifying block, %s", err.Error()) + } + + proposalBlk := blk.(snowman.OracleBlock) + options, err := proposalBlk.Options(context.Background()) + if err != nil { + return fmt.Errorf("failed retrieving options, %s", err.Error()) + } + + commit := options[0].(*blockexecutor.Block) + _, ok := commit.Block.(*blocks.BanffCommitBlock) + if !ok { + return fmt.Errorf("failed retrieving commit option, %s", err.Error()) + } + if err := blk.Accept(context.Background()); err != nil { + return fmt.Errorf("failed accepting block, %s", err.Error()) + } + + if err := commit.Verify(context.Background()); err != nil { + return fmt.Errorf("failed verifying commit block, %s", err.Error()) + } + if err := commit.Accept(context.Background()); err != nil { + return fmt.Errorf("failed accepting commit block, %s", err.Error()) + } + + if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { + return fmt.Errorf("failed verifying block, %s", err.Error()) + } + + // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + // require.ErrorIs(err, database.ErrNotFound) + return nil +} + +// buildTimestampsList creates validators start and end time, given the event list. +// output is returned as a list of state.Stakers, just because it's a convenient object to +// collect all relevant information. +// buildTimestampsList guarantees that start/end times of contiguous stakers do not overlap +func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.NodeID) ([]*state.Staker, error) { + res := make([]*state.Staker, 0, len(events)) + + currentTime = currentTime.Add(executor.SyncBound) + switch endTime := currentTime.Add(defaultMinStakingDuration); events[0] { + case startPrimaryWithBLS: + sk, err := bls.NewSecretKey() + if err != nil { + return nil, fmt.Errorf("could not make private key, %w", err) + } + + res = append(res, &state.Staker{ + NodeID: nodeID, + StartTime: currentTime, + EndTime: endTime, + PublicKey: bls.PublicFromSecretKey(sk), + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }) + case startPrimaryWithoutBLS: + res = append(res, &state.Staker{ + NodeID: nodeID, + StartTime: currentTime, + EndTime: endTime, + PublicKey: nil, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + }) + default: + return nil, fmt.Errorf("unexpected initial event %s", events[0]) + } + + // track current primary validator to make sure its staking period + // covers all of its subnet validators + currentPrimaryVal := res[0] + for i := 1; i < len(events); i++ { + currentTime = currentTime.Add(executor.SyncBound) + + switch currentEvent := events[i]; currentEvent { + case startSubnetValidator: + endTime := currentTime.Add(defaultMinStakingDuration) + val := &state.Staker{ + NodeID: nodeID, + StartTime: currentTime, + EndTime: endTime, + PublicKey: nil, + Priority: txs.SubnetPermissionlessValidatorCurrentPriority, + } + res = append(res, val) + + currentPrimaryVal.EndTime = endTime.Add(time.Second) + currentTime = endTime.Add(time.Second) + + case startPrimaryWithBLS: + currentTime = currentPrimaryVal.EndTime.Add(executor.SyncBound) + sk, err := bls.NewSecretKey() + if err != nil { + return nil, fmt.Errorf("could not make private key, %w", err) + } + + endTime := currentTime.Add(defaultMinStakingDuration) + val := &state.Staker{ + NodeID: nodeID, + StartTime: currentTime, + EndTime: endTime, + PublicKey: bls.PublicFromSecretKey(sk), + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + } + res = append(res, val) + currentPrimaryVal = val + + case startPrimaryWithoutBLS: + currentTime = currentPrimaryVal.EndTime.Add(executor.SyncBound) + endTime := currentTime.Add(defaultMinStakingDuration) + val := &state.Staker{ + NodeID: nodeID, + StartTime: currentTime, + EndTime: endTime, + PublicKey: nil, + Priority: txs.PrimaryNetworkValidatorCurrentPriority, + } + res = append(res, val) + currentPrimaryVal = val + } + } + return res, nil +} + +func TestTimestampListGenerator(t *testing.T) { + properties := gopter.NewProperties(nil) + + properties.Property("primary validators are returned in sequence", prop.ForAll( + func(events []string) string { + currentTime := time.Now() + nodeID := ids.GenerateTestNodeID() + validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) + if err != nil { + return fmt.Sprintf("failed building events sequence, %s", err.Error()) + } + + if len(validatorsTimes) == 0 { + return errZeroLenghtEventsList.Error() + } + + // nil out non subnet validators + subnetIndexes := make([]int, 0) + for idx, ev := range validatorsTimes { + if ev.Priority == txs.SubnetPermissionlessValidatorCurrentPriority { + subnetIndexes = append(subnetIndexes, idx) + } + } + for _, idx := range subnetIndexes { + validatorsTimes[idx] = nil + } + + currentEventTime := currentTime + for i, ev := range validatorsTimes { + if ev == nil { + continue // a subnet validator + } + if currentEventTime.After(ev.StartTime) { + return fmt.Sprintf("validator %d start time larger than current event time", i) + } + + if ev.StartTime.After(ev.EndTime) { + return fmt.Sprintf("validator %d start time larger than its end time", i) + } + + currentEventTime = ev.EndTime + } + + return "" + }, + gen.SliceOf(gen.OneConstOf( + startPrimaryWithBLS, + startPrimaryWithoutBLS, + startSubnetValidator, + )).SuchThat(func(v interface{}) bool { + list := v.([]string) + return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) + }), + ), + ) + + properties.Property("subnet validators are returned in sequence", prop.ForAll( + func(events []string) string { + currentTime := time.Now() + nodeID := ids.GenerateTestNodeID() + validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) + if err != nil { + return fmt.Sprintf("failed building events sequence, %s", err.Error()) + } + + if len(validatorsTimes) == 0 { + return errZeroLenghtEventsList.Error() + } + + // nil out non subnet validators + nonSubnetIndexes := make([]int, 0) + for idx, ev := range validatorsTimes { + if ev.Priority != txs.SubnetPermissionlessValidatorCurrentPriority { + nonSubnetIndexes = append(nonSubnetIndexes, idx) + } + } + for _, idx := range nonSubnetIndexes { + validatorsTimes[idx] = nil + } + + currentEventTime := currentTime + for i, ev := range validatorsTimes { + if ev == nil { + continue // a non-subnet validator + } + if currentEventTime.After(ev.StartTime) { + return fmt.Sprintf("validator %d start time larger than current event time", i) + } + + if ev.StartTime.After(ev.EndTime) { + return fmt.Sprintf("validator %d start time larger than its end time", i) + } + + currentEventTime = ev.EndTime + } + + return "" + }, + gen.SliceOf(gen.OneConstOf( + startPrimaryWithBLS, + startPrimaryWithoutBLS, + startSubnetValidator, + )).SuchThat(func(v interface{}) bool { + list := v.([]string) + return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) + }), + ), + ) + + properties.Property("subnet validators' times are bound by a primary validator's times", prop.ForAll( + func(events []string) string { + currentTime := time.Now() + nodeID := ids.GenerateTestNodeID() + validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) + if err != nil { + return fmt.Sprintf("failed building events sequence, %s", err.Error()) + } + + if len(validatorsTimes) == 0 { + return errZeroLenghtEventsList.Error() + } + + currentPrimaryValidator := validatorsTimes[0] + for i := 1; i < len(validatorsTimes); i++ { + if validatorsTimes[i].Priority != txs.SubnetPermissionlessValidatorCurrentPriority { + currentPrimaryValidator = validatorsTimes[i] + continue + } + + subnetVal := validatorsTimes[i] + if currentPrimaryValidator.StartTime.After(subnetVal.StartTime) || + subnetVal.EndTime.After(currentPrimaryValidator.EndTime) { + return "subnet validator not bounded by primary network ones" + } + } + return "" + }, + gen.SliceOf(gen.OneConstOf( + startPrimaryWithBLS, + startPrimaryWithoutBLS, + startSubnetValidator, + )).SuchThat(func(v interface{}) bool { + list := v.([]string) + return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) + }), + ), + ) + + properties.TestingRun(t) +} + +// add a single validator at the end of times, +// to make sure it won't pollute our tests +func buildVM() (*VM, ids.ID, error) { + vdrs := validators.NewManager() + primaryVdrs := validators.NewSet() + _ = vdrs.Add(constants.PrimaryNetworkID, primaryVdrs) + + forkTime := defaultGenesisTime + vm := &VM{Config: config.Config{ + Chains: chains.TestManager, + UptimeLockedCalculator: uptime.NewLockedCalculator(), + SybilProtectionEnabled: true, + Validators: vdrs, + TxFee: defaultTxFee, + CreateSubnetTxFee: 100 * defaultTxFee, + TransformSubnetTxFee: 100 * defaultTxFee, + CreateBlockchainTxFee: 100 * defaultTxFee, + MinValidatorStake: defaultMinValidatorStake, + MaxValidatorStake: defaultMaxValidatorStake, + MinDelegatorStake: defaultMinDelegatorStake, + MinStakeDuration: defaultMinStakingDuration, + MaxStakeDuration: defaultMaxStakingDuration, + RewardConfig: defaultRewardConfig, + ApricotPhase3Time: forkTime, + ApricotPhase5Time: forkTime, + BanffTime: forkTime, + CortinaTime: forkTime, + }} + vm.clock.Set(forkTime.Add(time.Second)) + + baseDBManager := manager.NewMemDB(version.Semantic1_0_0) + chainDBManager := baseDBManager.NewPrefixDBManager([]byte{0}) + atomicDB := prefixdb.New([]byte{1}, baseDBManager.Current().Database) + + msgChan := make(chan common.Message, 1) + ctx := defaultContext() + + m := atomic.NewMemory(atomicDB) + ctx.SharedMemory = m.NewSharedMemory(ctx.ChainID) + + ctx.Lock.Lock() + defer ctx.Lock.Unlock() + appSender := &common.SenderTest{} + appSender.CantSendAppGossip = true + appSender.SendAppGossipF = func(context.Context, []byte) error { + return nil + } + + genesisBytes, err := buildCustomGenesis() + if err != nil { + return nil, ids.Empty, err + } + + err = vm.Initialize( + context.Background(), + ctx, + chainDBManager, + genesisBytes, + nil, + nil, + msgChan, + nil, + appSender, + ) + if err != nil { + return nil, ids.Empty, err + } + + err = vm.SetState(context.Background(), snow.NormalOp) + if err != nil { + return nil, ids.Empty, err + } + + // Create a subnet and store it in testSubnet1 + // Note: following Banff activation, block acceptance will move + // chain time ahead + testSubnet1, err = vm.txBuilder.NewCreateSubnetTx( + 1, // threshold + []ids.ShortID{keys[0].PublicKey().Address()}, + []*secp256k1.PrivateKey{keys[len(keys)-1]}, // pays tx fee + keys[0].PublicKey().Address(), // change addr + ) + if err != nil { + return nil, ids.Empty, err + } + if err := vm.Builder.AddUnverifiedTx(testSubnet1); err != nil { + return nil, ids.Empty, err + } + + blk, err := vm.Builder.BuildBlock(context.Background()) + if err != nil { + return nil, ids.Empty, err + } + if err := blk.Verify(context.Background()); err != nil { + return nil, ids.Empty, err + } + if err := blk.Accept(context.Background()); err != nil { + return nil, ids.Empty, err + } + if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { + return nil, ids.Empty, err + } + + return vm, testSubnet1.ID(), nil +} + +func buildCustomGenesis() ([]byte, error) { + genesisUTXOs := make([]api.UTXO, len(keys)) + for i, key := range keys { + id := key.PublicKey().Address() + addr, err := address.FormatBech32(constants.UnitTestHRP, id.Bytes()) + if err != nil { + return nil, err + } + genesisUTXOs[i] = api.UTXO{ + Amount: json.Uint64(defaultBalance), + Address: addr, + } + } + + // we need at least a validator, otherwise BuildBlock would fail, since it won't find + // next staker to promote/evict from stakers set. Contrary to what happens with production code + // we push such validator at the end of times, so to avoid interference with our tests + nodeID := ids.NodeID(keys[len(keys)-1].PublicKey().Address()) + addr, err := address.FormatBech32(constants.UnitTestHRP, nodeID.Bytes()) + if err != nil { + return nil, err + } + + starTime := mockable.MaxTime.Add(-1 * defaultMinStakingDuration) + endTime := mockable.MaxTime + genesisValidator := api.PermissionlessValidator{ + Staker: api.Staker{ + StartTime: json.Uint64(starTime.Unix()), + EndTime: json.Uint64(endTime.Unix()), + NodeID: nodeID, + }, + RewardOwner: &api.Owner{ + Threshold: 1, + Addresses: []string{addr}, + }, + Staked: []api.UTXO{{ + Amount: json.Uint64(defaultWeight), + Address: addr, + }}, + DelegationFee: reward.PercentDenominator, + } + + buildGenesisArgs := api.BuildGenesisArgs{ + Encoding: formatting.Hex, + NetworkID: json.Uint32(constants.UnitTestID), + AvaxAssetID: avaxAssetID, + UTXOs: genesisUTXOs, + Validators: []api.PermissionlessValidator{genesisValidator}, + Chains: nil, + Time: json.Uint64(defaultGenesisTime.Unix()), + InitialSupply: json.Uint64(360 * units.MegaAvax), + } + + buildGenesisResponse := api.BuildGenesisReply{} + platformvmSS := api.StaticService{} + if err := platformvmSS.BuildGenesis(nil, &buildGenesisArgs, &buildGenesisResponse); err != nil { + return nil, err + } + + genesisBytes, err := formatting.Decode(buildGenesisResponse.Encoding, buildGenesisResponse.Bytes) + if err != nil { + return nil, err + } + + return genesisBytes, nil +} diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index 6a81de2d6348..db27955bdbc7 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -5,6 +5,8 @@ package platformvm import ( "context" + "encoding/hex" + "errors" "testing" "time" @@ -25,16 +27,20 @@ import ( "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/reward" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/utxo" "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) @@ -1442,3 +1448,758 @@ func TestRemovePermissionedValidatorDuringPendingToCurrentTransitionTracked(t *t require.NoError(removeSubnetValidatorBlock.Accept(context.Background())) require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) } + +// GetValidatorSet must return the BLS keys for a given validator +// even in case it has already expired +func Test_RegressionBLSKeyDiff(t *testing.T) { + // setup + require := require.New(t) + vm, _, _ := defaultVM() + vm.ctx.Lock.Lock() + defer func() { + require.NoError(vm.Shutdown(context.Background())) + + vm.ctx.Lock.Unlock() + }() + subnetID := testSubnet1.TxID + + // setup heights/time + currentTime := defaultGenesisTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + // A subnet validator stake and then stop; also its primary network counterpart stops staking + var ( + primaryStartTime = currentTime.Add(executor.SyncBound) + subnetStartTime = primaryStartTime.Add(executor.SyncBound) + subnetEndTime = subnetStartTime.Add(defaultMinStakingDuration) + primaryEndTime = subnetEndTime.Add(time.Second) + primaryReStartTime = primaryEndTime.Add(executor.SyncBound) + primaryReEndTime = primaryReStartTime.Add(defaultMinStakingDuration) + ) + + // insert primary network validator + nodeID := ids.GenerateTestNodeID() + addr := keys[0].PublicKey().Address() + skBytes, err := hex.DecodeString("6668fecd4595b81e4d568398c820bbf3f073cb222902279fa55ebb84764ed2e3") + require.NoError(err) + sk1, err := bls.SecretKeyFromBytes(skBytes) + require.NoError(err) + + // build primary network validator with BLS key + utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) + ins, unstakedOuts, stakedOuts, signers, err := utxoHandler.Spend( + vm.state, + keys, + vm.MinValidatorStake, + vm.Config.AddPrimaryNetworkValidatorFee, + addr, // change Addresss + ) + require.NoError(err) + + uPrimaryTx := &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: unstakedOuts, + }}, + Validator: txs.Validator{ + NodeID: nodeID, + Start: uint64(primaryStartTime.Unix()), + End: uint64(primaryEndTime.Unix()), + Wght: vm.MinValidatorStake, + }, + Subnet: constants.PrimaryNetworkID, + Signer: signer.NewProofOfPossession(sk1), + StakeOuts: stakedOuts, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegationShares: reward.PercentDenominator, + } + primaryTx, err := txs.NewSigned(uPrimaryTx, txs.Codec, signers) + require.NoError(err) + require.NoError(primaryTx.SyntacticVerify(vm.ctx)) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryTx)) + blk, err := vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting primary validator to current + currentTime = primaryStartTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // insert the subnet validator + subnetTx, err := vm.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetStartTime.Unix()), // Start time + uint64(subnetEndTime.Unix()), // end time + nodeID, // Node ID + subnetID, + []*secp256k1.PrivateKey{keys[0], keys[1]}, + addr, + ) + require.NoError(err) + + require.NoError(vm.Builder.AddUnverifiedTx(subnetTx)) + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting the subnet validator to current + currentTime = subnetStartTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(subnetID, nodeID) + require.NoError(err) + + subnetStartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // move time ahead, terminating the subnet validator + currentTime = subnetEndTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(subnetID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + subnetEndHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // move time ahead, terminating primary network validator + currentTime = primaryEndTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + + proposalBlk := blk.(snowman.OracleBlock) + options, err := proposalBlk.Options(context.Background()) + require.NoError(err) + + commit := options[0].(*blockexecutor.Block) + require.IsType(&blocks.BanffCommitBlock{}, commit.Block) + + require.NoError(blk.Accept(context.Background())) + require.NoError(commit.Verify(context.Background())) + require.NoError(commit.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // reinsert primary validator with a different BLS key + sk2, err := bls.NewSecretKey() + require.NoError(err) + require.NotEqual(sk1, sk2) + + ins, unstakedOuts, stakedOuts, signers, err = utxoHandler.Spend( + vm.state, + keys, + vm.MinValidatorStake, + vm.Config.AddPrimaryNetworkValidatorFee, + addr, // change Addresss + ) + require.NoError(err) + + uPrimaryRestartTx := &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: unstakedOuts, + }}, + Validator: txs.Validator{ + NodeID: nodeID, + Start: uint64(primaryReStartTime.Unix()), + End: uint64(primaryReEndTime.Unix()), + Wght: vm.MinValidatorStake, + }, + Subnet: constants.PrimaryNetworkID, + Signer: signer.NewProofOfPossession(sk2), + StakeOuts: stakedOuts, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegationShares: reward.PercentDenominator, + } + primaryRestartTx, err := txs.NewSigned(uPrimaryRestartTx, txs.Codec, signers) + require.NoError(err) + require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting restarted primary validator to current + currentTime = primaryReStartTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + primaryRestartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // Show that validators are rebuilt with the right BLS key + for height := primaryStartHeight; height < primaryEndHeight; height++ { + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + constants.PrimaryNetworkID, + height, + uPrimaryTx.Signer.Key()), + ) + } + for height := primaryEndHeight; height < primaryRestartHeight; height++ { + require.ErrorIs(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + constants.PrimaryNetworkID, + primaryEndHeight, + uPrimaryTx.Signer.Key()), + database.ErrNotFound, + ) + } + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + constants.PrimaryNetworkID, + primaryRestartHeight, + uPrimaryRestartTx.Signer.Key()), + ) + + for height := subnetStartHeight; height < subnetEndHeight; height++ { + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + subnetID, + height, + uPrimaryTx.Signer.Key()), + ) + } + + for height := subnetEndHeight; height <= primaryRestartHeight; height++ { + require.ErrorIs(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + subnetID, + primaryEndHeight, + uPrimaryTx.Signer.Key()), + database.ErrNotFound, + ) + } +} + +func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { + // A primary network validator has an empty BLS key. Then it restakes + // adding the BLS key. Querying the validator set back when BLS key was empty + // must return an empty BLS key. + + // setup + require := require.New(t) + vm, _, _ := defaultVM() + vm.ctx.Lock.Lock() + defer func() { + require.NoError(vm.Shutdown(context.Background())) + + vm.ctx.Lock.Unlock() + }() + + // setup heights/time + currentTime := defaultGenesisTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + // A primary network validator stake twice + var ( + primaryStartTime1 = currentTime.Add(executor.SyncBound) + primaryEndTime1 = primaryStartTime1.Add(defaultMinStakingDuration) + primaryStartTime2 = primaryEndTime1.Add(executor.SyncBound) + primaryEndTime2 = primaryStartTime2.Add(defaultMinStakingDuration) + ) + + // Add a primary network validator with no BLS key + nodeID := ids.GenerateTestNodeID() + addr := keys[0].PublicKey().Address() + primaryTx1, err := vm.txBuilder.NewAddValidatorTx( + vm.MinValidatorStake, + uint64(primaryStartTime1.Unix()), + uint64(primaryEndTime1.Unix()), + nodeID, + addr, + reward.PercentDenominator, + []*secp256k1.PrivateKey{keys[0]}, + addr, + ) + require.NoError(err) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryTx1)) + blk, err := vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting primary validator to current + currentTime = primaryStartTime1 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // move time ahead, terminating primary network validator + currentTime = primaryEndTime1 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + + proposalBlk := blk.(snowman.OracleBlock) + options, err := proposalBlk.Options(context.Background()) + require.NoError(err) + + commit := options[0].(*blockexecutor.Block) + require.IsType(&blocks.BanffCommitBlock{}, commit.Block) + + require.NoError(blk.Accept(context.Background())) + require.NoError(commit.Verify(context.Background())) + require.NoError(commit.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // reinsert primary validator with a different BLS key + sk2, err := bls.NewSecretKey() + require.NoError(err) + + utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) + ins, unstakedOuts, stakedOuts, signers, err := utxoHandler.Spend( + vm.state, + keys, + vm.MinValidatorStake, + vm.Config.AddPrimaryNetworkValidatorFee, + addr, // change Addresss + ) + require.NoError(err) + + uPrimaryRestartTx := &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: unstakedOuts, + }}, + Validator: txs.Validator{ + NodeID: nodeID, + Start: uint64(primaryStartTime2.Unix()), + End: uint64(primaryEndTime2.Unix()), + Wght: vm.MinValidatorStake, + }, + Subnet: constants.PrimaryNetworkID, + Signer: signer.NewProofOfPossession(sk2), + StakeOuts: stakedOuts, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegationShares: reward.PercentDenominator, + } + primaryRestartTx, err := txs.NewSigned(uPrimaryRestartTx, txs.Codec, signers) + require.NoError(err) + require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting restarted primary validator to current + currentTime = primaryStartTime2 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + emptySigner := &signer.Empty{} + for height := primaryStartHeight; height < primaryEndHeight; height++ { + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + constants.PrimaryNetworkID, + height, + emptySigner.Key()), + ) + } +} + +func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { + // A primary network validator has an empty BLS key and a subnet validator. + // Primary network validator terminates its first staking cycle and it restakes + // adding the BLS key. Querying the validator set back when BLS key was empty + // must return an empty BLS key for the subnet validator + + // setup + require := require.New(t) + vm, _, _ := defaultVM() + vm.ctx.Lock.Lock() + defer func() { + require.NoError(vm.Shutdown(context.Background())) + + vm.ctx.Lock.Unlock() + }() + subnetID := testSubnet1.TxID + + // setup heights/time + currentTime := defaultGenesisTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + // A primary network validator stake twice + var ( + primaryStartTime1 = currentTime.Add(executor.SyncBound) + subnetStartTime = primaryStartTime1.Add(executor.SyncBound) + subnetEndTime = subnetStartTime.Add(defaultMinStakingDuration) + primaryEndTime1 = subnetEndTime.Add(time.Second) + primaryStartTime2 = primaryEndTime1.Add(executor.SyncBound) + primaryEndTime2 = primaryStartTime2.Add(defaultMinStakingDuration) + ) + + // Add a primary network validator with no BLS key + nodeID := ids.GenerateTestNodeID() + addr := keys[0].PublicKey().Address() + primaryTx1, err := vm.txBuilder.NewAddValidatorTx( + vm.MinValidatorStake, + uint64(primaryStartTime1.Unix()), + uint64(primaryEndTime1.Unix()), + nodeID, + addr, + reward.PercentDenominator, + []*secp256k1.PrivateKey{keys[0]}, + addr, + ) + require.NoError(err) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryTx1)) + blk, err := vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting primary validator to current + currentTime = primaryStartTime1 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + primaryStartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // insert the subnet validator + subnetTx, err := vm.txBuilder.NewAddSubnetValidatorTx( + 1, // Weight + uint64(subnetStartTime.Unix()), // Start time + uint64(subnetEndTime.Unix()), // end time + nodeID, // Node ID + subnetID, + []*secp256k1.PrivateKey{keys[0], keys[1]}, + addr, + ) + require.NoError(err) + + require.NoError(vm.Builder.AddUnverifiedTx(subnetTx)) + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting the subnet validator to current + currentTime = subnetStartTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(subnetID, nodeID) + require.NoError(err) + + subnetStartHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // move time ahead, terminating the subnet validator + currentTime = subnetEndTime + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(subnetID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + subnetEndHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // move time ahead, terminating primary network validator + currentTime = primaryEndTime1 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + + proposalBlk := blk.(snowman.OracleBlock) + options, err := proposalBlk.Options(context.Background()) + require.NoError(err) + + commit := options[0].(*blockexecutor.Block) + require.IsType(&blocks.BanffCommitBlock{}, commit.Block) + + require.NoError(blk.Accept(context.Background())) + require.NoError(commit.Verify(context.Background())) + require.NoError(commit.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.ErrorIs(err, database.ErrNotFound) + + primaryEndHeight, err := vm.GetCurrentHeight(context.Background()) + require.NoError(err) + + // reinsert primary validator with a different BLS key + sk2, err := bls.NewSecretKey() + require.NoError(err) + + utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) + ins, unstakedOuts, stakedOuts, signers, err := utxoHandler.Spend( + vm.state, + keys, + vm.MinValidatorStake, + vm.Config.AddPrimaryNetworkValidatorFee, + addr, // change Addresss + ) + require.NoError(err) + + uPrimaryRestartTx := &txs.AddPermissionlessValidatorTx{ + BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ + NetworkID: vm.ctx.NetworkID, + BlockchainID: vm.ctx.ChainID, + Ins: ins, + Outs: unstakedOuts, + }}, + Validator: txs.Validator{ + NodeID: nodeID, + Start: uint64(primaryStartTime2.Unix()), + End: uint64(primaryEndTime2.Unix()), + Wght: vm.MinValidatorStake, + }, + Subnet: constants.PrimaryNetworkID, + Signer: signer.NewProofOfPossession(sk2), + StakeOuts: stakedOuts, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{ + addr, + }, + }, + DelegationShares: reward.PercentDenominator, + } + primaryRestartTx, err := txs.NewSigned(uPrimaryRestartTx, txs.Codec, signers) + require.NoError(err) + require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) + + require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + // move time ahead, promoting restarted primary validator to current + currentTime = primaryStartTime2 + vm.clock.Set(currentTime) + vm.state.SetTimestamp(currentTime) + + blk, err = vm.Builder.BuildBlock(context.Background()) + require.NoError(err) + require.NoError(blk.Verify(context.Background())) + require.NoError(blk.Accept(context.Background())) + require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + + _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) + require.NoError(err) + + emptySigner := &signer.Empty{} + for height := primaryStartHeight; height < primaryEndHeight; height++ { + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + constants.PrimaryNetworkID, + height, + emptySigner.Key()), + ) + } + for height := subnetStartHeight; height < subnetEndHeight; height++ { + require.NoError(checkValidatorBlsKeyIsSet( + vm.State, + nodeID, + subnetID, + height, + emptySigner.Key()), + ) + } +} + +func checkValidatorBlsKeyIsSet( + valState validators.State, + nodeID ids.NodeID, + subnetID ids.ID, + height uint64, + expectedBlsKey *bls.PublicKey, +) error { + vals, err := valState.GetValidatorSet(context.Background(), height, subnetID) + if err != nil { + return err + } + + val, found := vals[nodeID] + if !found { + return database.ErrNotFound + } + if val.PublicKey != expectedBlsKey { + return errors.New("unexpected BLS key") + } + + return nil +} From b608a164b44b5afacd817219b321e810a1e4749c Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:31:12 -0400 Subject: [PATCH 02/27] nits --- vms/platformvm/validators/manager.go | 9 +++------ vms/platformvm/validators_set_property_test.go | 9 +++++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 35d48aa42243..8d26e315c400 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -195,8 +195,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { - err := m.applyValidatorDiffs(subnetSet, primarySet, subnetID, diffHeight) - if err != nil { + if err := m.applyValidatorDiffs(subnetSet, primarySet, subnetID, diffHeight); err != nil { return nil, err } } @@ -222,8 +221,7 @@ func (m *manager) applyValidatorDiffs( return err } for nodeID, weightDiff := range primaryWeightDiffs { - err := rebuildWeight(primarySet, nodeID, weightDiff) - if err != nil { + if err := rebuildWeight(primarySet, nodeID, weightDiff); err != nil { return err } } @@ -246,8 +244,7 @@ func (m *manager) applyValidatorDiffs( return err } for nodeID, weightDiff := range targetWeightDiffs { - err := rebuildWeight(targetSet, nodeID, weightDiff) - if err != nil { + if err := rebuildWeight(targetSet, nodeID, weightDiff); err != nil { return err } } diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index d96f0f35541a..10fd937aaeb4 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -11,6 +11,10 @@ import ( "testing" "time" + "github.com/leanovate/gopter" + "github.com/leanovate/gopter/gen" + "github.com/leanovate/gopter/prop" + "github.com/ava-labs/avalanchego/chains" "github.com/ava-labs/avalanchego/chains/atomic" "github.com/ava-labs/avalanchego/database/manager" @@ -41,9 +45,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/utxo" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/leanovate/gopter" - "github.com/leanovate/gopter/gen" - "github.com/leanovate/gopter/prop" blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" ) @@ -80,7 +81,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - // build validator sequence out of random (tey reproducible) input + // build validator sequence out of random (yet reproducible) input validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { return fmt.Sprintf("failed building events sequence, %s", err.Error()) From 6bc2ed034ff8c3a3413b2bdcd7807c978a6183fc Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:43:55 -0400 Subject: [PATCH 03/27] nit --- vms/platformvm/validators/manager.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 8d26e315c400..bf78a05cdc7e 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -221,7 +221,7 @@ func (m *manager) applyValidatorDiffs( return err } for nodeID, weightDiff := range primaryWeightDiffs { - if err := rebuildWeight(primarySet, nodeID, weightDiff); err != nil { + if err := applyWeightDiff(primarySet, nodeID, weightDiff); err != nil { return err } } @@ -244,7 +244,7 @@ func (m *manager) applyValidatorDiffs( return err } for nodeID, weightDiff := range targetWeightDiffs { - if err := rebuildWeight(targetSet, nodeID, weightDiff); err != nil { + if err := applyWeightDiff(targetSet, nodeID, weightDiff); err != nil { return err } } @@ -259,7 +259,7 @@ func (m *manager) applyValidatorDiffs( return nil } -func rebuildWeight( +func applyWeightDiff( targetSet map[ids.NodeID]*validators.GetValidatorOutput, nodeID ids.NodeID, weightDiff *state.ValidatorWeightDiff, From 04d4e9cfed8c6908f814460a53edc5c669661e87 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:54:27 -0400 Subject: [PATCH 04/27] simplify --- vms/platformvm/validators/manager.go | 23 +++++++++-------------- vms/platformvm/validators/manager_test.go | 10 ++++++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index bf78a05cdc7e..6d1911cdb23c 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -164,15 +164,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return nil, err } } - currentSubnetValidatorList := currentSubnetValidators.List() - subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) - for _, vdr := range currentSubnetValidatorList { - subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ - NodeID: vdr.NodeID, - // PublicKey will be picked from primary validators - Weight: vdr.Weight, - } - } + subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, currentSubnetValidators.Len()) currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { @@ -182,16 +174,19 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i currentPrimaryValidatorList := currentPrimaryNetworkValidators.List() primarySet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentPrimaryValidatorList)) for _, vdr := range currentPrimaryValidatorList { + if currentSubnetValidators.Contains(vdr.NodeID) { + subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ + NodeID: vdr.NodeID, + PublicKey: vdr.PublicKey, + Weight: vdr.Weight, + } + } + primarySet[vdr.NodeID] = &validators.GetValidatorOutput{ NodeID: vdr.NodeID, PublicKey: vdr.PublicKey, Weight: vdr.Weight, } - - // fill PK from primary network - if _, found := subnetSet[vdr.NodeID]; found { - subnetSet[vdr.NodeID].PublicKey = vdr.PublicKey - } } for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 43539771775a..acb173e099aa 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -397,6 +397,16 @@ func TestVM_GetValidatorSet(t *testing.T) { mockSubnetVdrSet = validators.NewMockSet(ctrl) mockSubnetVdrSet.EXPECT().List().Return(tt.currentSubnetValidators).AnyTimes() } + mockSubnetVdrSet.EXPECT().Len().Return(len(tt.currentSubnetValidators)).AnyTimes() + mockSubnetVdrSet.EXPECT().Contains(gomock.Any()).DoAndReturn(func(nodeID ids.NodeID) bool { + for _, vdr := range tt.currentSubnetValidators { + if vdr.NodeID == nodeID { + return true + } + } + + return false + }).AnyTimes() vdrs.EXPECT().Get(tt.subnetID).Return(mockSubnetVdrSet, true).AnyTimes() for _, vdr := range testValidators { From c509fe1d47b3e7a1426691f7ad6866ef82f2fa01 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 16:29:23 -0400 Subject: [PATCH 05/27] Revert "simplify" This reverts commit 04d4e9cfed8c6908f814460a53edc5c669661e87. --- vms/platformvm/validators/manager.go | 23 ++++++++++++++--------- vms/platformvm/validators/manager_test.go | 10 ---------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 6d1911cdb23c..bf78a05cdc7e 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -164,7 +164,15 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return nil, err } } - subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, currentSubnetValidators.Len()) + currentSubnetValidatorList := currentSubnetValidators.List() + subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) + for _, vdr := range currentSubnetValidatorList { + subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ + NodeID: vdr.NodeID, + // PublicKey will be picked from primary validators + Weight: vdr.Weight, + } + } currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { @@ -174,19 +182,16 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i currentPrimaryValidatorList := currentPrimaryNetworkValidators.List() primarySet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentPrimaryValidatorList)) for _, vdr := range currentPrimaryValidatorList { - if currentSubnetValidators.Contains(vdr.NodeID) { - subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ - NodeID: vdr.NodeID, - PublicKey: vdr.PublicKey, - Weight: vdr.Weight, - } - } - primarySet[vdr.NodeID] = &validators.GetValidatorOutput{ NodeID: vdr.NodeID, PublicKey: vdr.PublicKey, Weight: vdr.Weight, } + + // fill PK from primary network + if _, found := subnetSet[vdr.NodeID]; found { + subnetSet[vdr.NodeID].PublicKey = vdr.PublicKey + } } for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index acb173e099aa..43539771775a 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -397,16 +397,6 @@ func TestVM_GetValidatorSet(t *testing.T) { mockSubnetVdrSet = validators.NewMockSet(ctrl) mockSubnetVdrSet.EXPECT().List().Return(tt.currentSubnetValidators).AnyTimes() } - mockSubnetVdrSet.EXPECT().Len().Return(len(tt.currentSubnetValidators)).AnyTimes() - mockSubnetVdrSet.EXPECT().Contains(gomock.Any()).DoAndReturn(func(nodeID ids.NodeID) bool { - for _, vdr := range tt.currentSubnetValidators { - if vdr.NodeID == nodeID { - return true - } - } - - return false - }).AnyTimes() vdrs.EXPECT().Get(tt.subnetID).Return(mockSubnetVdrSet, true).AnyTimes() for _, vdr := range testValidators { From 8df0224928aa859fd263813758fc4f858b6b963e Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:43:27 -0400 Subject: [PATCH 06/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 10fd937aaeb4..d16ff0a5d847 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -442,7 +442,9 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { // buildTimestampsList creates validators start and end time, given the event list. // output is returned as a list of state.Stakers, just because it's a convenient object to // collect all relevant information. -// buildTimestampsList guarantees that start/end times of contiguous stakers do not overlap +// buildTimestampsList creates validators start and end time, given the event list. +// output is returned as a list of state.Stakers, just because it's a convenient object to +// collect all relevant information. func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.NodeID) ([]*state.Staker, error) { res := make([]*state.Staker, 0, len(events)) From 5831ec4b6c2a3cfa037dc0315d60ad25749ca79e Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:43:46 -0400 Subject: [PATCH 07/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index d16ff0a5d847..78ec4e7c8a0f 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -365,7 +365,7 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed verifying block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed getting last accepted block, %s", err.Error()) } return vm.state.GetCurrentValidator(stakerTx.SubnetID(), stakerTx.NodeID()) From 8430ee1ec21aeb4b1bbfb966260cef5a7a5bddf9 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:43:52 -0400 Subject: [PATCH 08/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 78ec4e7c8a0f..b6c3d2e99f78 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -343,7 +343,7 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed verifying block, %s", err.Error()) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return nil, fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { return nil, fmt.Errorf("failed verifying block, %s", err.Error()) From 284000d9f4cb7ddd220de593604eceeecf3a4b9d Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:44:02 -0400 Subject: [PATCH 09/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index b6c3d2e99f78..6b7e7ceb0077 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -387,7 +387,7 @@ func terminateSubnetValidator(vm *VM, validator *state.Staker) error { return fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed getting last accepted block, %s", err.Error()) } // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) From ae6e944df3c5a22fd1c5d2e00a3406b87a841e29 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:44:08 -0400 Subject: [PATCH 10/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 6b7e7ceb0077..db0d83f57510 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -346,7 +346,7 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return nil, fmt.Errorf("failed getting last accepted block, %s", err.Error()) } // move time ahead, promoting the validator to current From e51ab317f379d42971cb4fbf270c100de5345b5c Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:44:15 -0400 Subject: [PATCH 11/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index db0d83f57510..1c775d198864 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -306,7 +306,7 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, e return nil, fmt.Errorf("could not create AddPermissionlessValidatorTx with BLS key, %w", err) } if err := signedTx.SyntacticVerify(vm.ctx); err != nil { - return nil, fmt.Errorf("failed sintax verification of AddPermissionlessValidatorTx, %w", err) + return nil, fmt.Errorf("failed syntax verification of AddPermissionlessValidatorTx, %w", err) } return internalAddValidator(vm, signedTx) } From 33cae5441cf7a0d719540a2744c85775d15f5f86 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:44:40 -0400 Subject: [PATCH 12/27] Update vms/platformvm/validators_set_property_test.go Co-authored-by: Sam Batschelet Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 1c775d198864..071e5c18895e 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -362,7 +362,7 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed verifying block, %s", err.Error()) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { return fmt.Errorf("failed getting last accepted block, %s", err.Error()) From d63f5dfa82e436c4d56d09ba816eb0ab853a1b68 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:25:42 -0400 Subject: [PATCH 13/27] Update vms/platformvm/vm_regression_test.go Co-authored-by: Darioush Jalali Signed-off-by: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> --- vms/platformvm/vm_regression_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index db27955bdbc7..3669b0044a13 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -1450,7 +1450,7 @@ func TestRemovePermissionedValidatorDuringPendingToCurrentTransitionTracked(t *t } // GetValidatorSet must return the BLS keys for a given validator -// even in case it has already expired +// correctly when queried at a previous height, even in case it has currently expired func Test_RegressionBLSKeyDiff(t *testing.T) { // setup require := require.New(t) From b71c0bcd289ab614b5c8d118fd5afd1c05d0051e Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:45:34 -0400 Subject: [PATCH 14/27] nits --- vms/platformvm/validators_set_property_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 071e5c18895e..4abc91c9451c 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -81,7 +81,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - // build validator sequence out of random (yet reproducible) input + // build validator sequences out of pseudo-random events validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { return fmt.Sprintf("failed building events sequence, %s", err.Error()) From 1db79967f22edd9b820574c012560fd25430aa86 Mon Sep 17 00:00:00 2001 From: Joshua Kim <20001595+joshua-kim@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:27:18 -0400 Subject: [PATCH 15/27] nits --- vms/platformvm/validators_set_property_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 4abc91c9451c..eabbf3866a2f 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -306,7 +306,7 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, e return nil, fmt.Errorf("could not create AddPermissionlessValidatorTx with BLS key, %w", err) } if err := signedTx.SyntacticVerify(vm.ctx); err != nil { - return nil, fmt.Errorf("failed syntax verification of AddPermissionlessValidatorTx, %w", err) + return nil, fmt.Errorf("failed syntax verification of AddPermissionlessValidatorTx, %w", err) } return internalAddValidator(vm, signedTx) } @@ -346,7 +346,7 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed getting last accepted block, %s", err.Error()) + return nil, fmt.Errorf("failed setting preference, %s", err.Error()) } // move time ahead, promoting the validator to current @@ -362,10 +362,10 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { return nil, fmt.Errorf("failed verifying block, %s", err.Error()) } if err := blk.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting block, %s", err.Error()) + return nil, fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed getting last accepted block, %s", err.Error()) + return nil, fmt.Errorf("failed setting preference, %s", err.Error()) } return vm.state.GetCurrentValidator(stakerTx.SubnetID(), stakerTx.NodeID()) @@ -387,7 +387,7 @@ func terminateSubnetValidator(vm *VM, validator *state.Staker) error { return fmt.Errorf("failed accepting block, %s", err.Error()) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed getting last accepted block, %s", err.Error()) + return fmt.Errorf("failed setting preference, %s", err.Error()) } // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) @@ -431,7 +431,7 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed setting preference, %s", err.Error()) } // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) From 071559d882c323f668c4fe259a4f0235eccff3a4 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 08:49:34 +0200 Subject: [PATCH 16/27] nit --- vms/platformvm/validators_set_property_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index eabbf3866a2f..7e2a78934a95 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -390,8 +390,6 @@ func terminateSubnetValidator(vm *VM, validator *state.Staker) error { return fmt.Errorf("failed setting preference, %s", err.Error()) } - // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) - // require.ErrorIs(err, database.ErrNotFound) return nil } @@ -434,14 +432,9 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { return fmt.Errorf("failed setting preference, %s", err.Error()) } - // _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) - // require.ErrorIs(err, database.ErrNotFound) return nil } -// buildTimestampsList creates validators start and end time, given the event list. -// output is returned as a list of state.Stakers, just because it's a convenient object to -// collect all relevant information. // buildTimestampsList creates validators start and end time, given the event list. // output is returned as a list of state.Stakers, just because it's a convenient object to // collect all relevant information. From 0c96716677a80acef886c8446aa9f63099eaed46 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 09:18:07 +0200 Subject: [PATCH 17/27] nits --- .../validators_set_property_test.go | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 7e2a78934a95..ea86fb05b2ed 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -55,7 +55,7 @@ const ( startSubnetValidator = "subnet-validator\n" ) -var errZeroLenghtEventsList = errors.New("unexpected zero length for events") +var errEmptyEventsList = errors.New("empty events list") func TestGetValidatorsSetProperty(t *testing.T) { properties := gopter.NewProperties(nil) @@ -163,10 +163,10 @@ func TestGetValidatorsSetProperty(t *testing.T) { for subnet, validatorsSet := range subnetSets { res, err := vm.GetValidatorSet(context.Background(), height, subnet) if err != nil { - return fmt.Sprintf("failed GetValidatorSet, %v", err) + return fmt.Sprintf("failed GetValidatorSet, %s", err.Error()) } if !reflect.DeepEqual(validatorsSet, res) { - return fmt.Sprintf("failed validators set comparison, %v", err) + return fmt.Sprintf("failed validators set comparison, %s", err.Error()) } } } @@ -216,7 +216,7 @@ func takeValidatorsSnapshotAtCurrentHeight(vm *VM, validatorsSetByHeightAndSubne } blsKey := v.PublicKey - if v.SubnetID != constants.PlatformChainID { + if v.SubnetID != constants.PrimaryNetworkID { // pick bls key from primary validator s, err := vm.state.GetCurrentValidator(constants.PlatformChainID, v.NodeID) if err != nil { @@ -262,7 +262,7 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, e addr, // change Addresss ) if err != nil { - return nil, fmt.Errorf("could not create inputs/ourputs for permissionless validator, %w", err) + return nil, fmt.Errorf("could not create inputs/outputs for permissionless validator, %w", err) } sk, err := bls.NewSecretKey() if err != nil { @@ -332,21 +332,21 @@ func addPrimaryValidatorWithoutBLSKey(vm *VM, data *state.Staker) (*state.Staker func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { stakerTx := signedTx.Unsigned.(txs.StakerTx) if err := vm.Builder.AddUnverifiedTx(signedTx); err != nil { - return nil, fmt.Errorf("could not add tx to mempool, %s", err.Error()) + return nil, fmt.Errorf("could not add tx to mempool, %w", err) } blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return nil, fmt.Errorf("failed building block, %s", err.Error()) + return nil, fmt.Errorf("failed building block, %w", err) } if err := blk.Verify(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return nil, fmt.Errorf("failed verifying block, %w", err) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed accepting block, %s", err.Error()) + return nil, fmt.Errorf("failed accepting block, %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed setting preference, %s", err.Error()) + return nil, fmt.Errorf("failed setting preference, %w", err) } // move time ahead, promoting the validator to current @@ -356,16 +356,16 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { blk, err = vm.Builder.BuildBlock(context.Background()) if err != nil { - return nil, fmt.Errorf("failed building block, %s", err.Error()) + return nil, fmt.Errorf("failed building block, %w", err) } if err := blk.Verify(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %s", err.Error()) + return nil, fmt.Errorf("failed verifying block, %w", err) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed accepting block, %s", err.Error()) + return nil, fmt.Errorf("failed accepting block, %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed setting preference, %s", err.Error()) + return nil, fmt.Errorf("failed setting preference, %w", err) } return vm.state.GetCurrentValidator(stakerTx.SubnetID(), stakerTx.NodeID()) @@ -378,16 +378,16 @@ func terminateSubnetValidator(vm *VM, validator *state.Staker) error { blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return fmt.Errorf("failed building block, %s", err.Error()) + return fmt.Errorf("failed building block, %w", err) } if err := blk.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed verifying block, %w", err) } if err := blk.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting block, %s", err.Error()) + return fmt.Errorf("failed accepting block, %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed setting preference, %s", err.Error()) + return fmt.Errorf("failed setting preference, %w", err) } return nil @@ -400,36 +400,36 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return fmt.Errorf("failed building block, %s", err.Error()) + return fmt.Errorf("failed building block, %w", err) } if err := blk.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying block, %s", err.Error()) + return fmt.Errorf("failed verifying block, %w", err) } proposalBlk := blk.(snowman.OracleBlock) options, err := proposalBlk.Options(context.Background()) if err != nil { - return fmt.Errorf("failed retrieving options, %s", err.Error()) + return fmt.Errorf("failed retrieving options, %w", err) } commit := options[0].(*blockexecutor.Block) _, ok := commit.Block.(*blocks.BanffCommitBlock) if !ok { - return fmt.Errorf("failed retrieving commit option, %s", err.Error()) + return fmt.Errorf("failed retrieving commit option, %w", err) } if err := blk.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting block, %s", err.Error()) + return fmt.Errorf("failed accepting block, %w", err) } if err := commit.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying commit block, %s", err.Error()) + return fmt.Errorf("failed verifying commit block, %w", err) } if err := commit.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting commit block, %s", err.Error()) + return fmt.Errorf("failed accepting commit block, %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed setting preference, %s", err.Error()) + return fmt.Errorf("failed setting preference, %w", err) } return nil @@ -537,7 +537,7 @@ func TestTimestampListGenerator(t *testing.T) { } if len(validatorsTimes) == 0 { - return errZeroLenghtEventsList.Error() + return errEmptyEventsList.Error() } // nil out non subnet validators @@ -590,7 +590,7 @@ func TestTimestampListGenerator(t *testing.T) { } if len(validatorsTimes) == 0 { - return errZeroLenghtEventsList.Error() + return errEmptyEventsList.Error() } // nil out non subnet validators @@ -643,7 +643,7 @@ func TestTimestampListGenerator(t *testing.T) { } if len(validatorsTimes) == 0 { - return errZeroLenghtEventsList.Error() + return errEmptyEventsList.Error() } currentPrimaryValidator := validatorsTimes[0] From b89776404b5f3bb76b01fc6fd82fc39a92fac3e7 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 10:04:58 +0200 Subject: [PATCH 18/27] nits --- vms/platformvm/validators_set_property_test.go | 5 +++++ vms/platformvm/vm_regression_test.go | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index ea86fb05b2ed..232b8ee09f3f 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -57,6 +57,11 @@ const ( var errEmptyEventsList = errors.New("empty events list") +// for a given (permissioned) subnet, the test stakes and restakes multiple times +// a node as a primary and subnet validator. The BLS key of the node is changed across +// staking periods, and it can even be nil. We test that GetValidatorSet returns +// the correct primary and subnet validators data, with the right BLS key version at +// all relevant heights. func TestGetValidatorsSetProperty(t *testing.T) { properties := gopter.NewProperties(nil) diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index 3669b0044a13..48add26f44f7 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -1463,12 +1463,12 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { }() subnetID := testSubnet1.TxID - // setup heights/time + // setup time currentTime := defaultGenesisTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - // A subnet validator stake and then stop; also its primary network counterpart stops staking + // A subnet validator stakes and then stops; also its primary network counterpart stops staking var ( primaryStartTime = currentTime.Add(executor.SyncBound) subnetStartTime = primaryStartTime.Add(executor.SyncBound) @@ -1776,7 +1776,7 @@ func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { vm.ctx.Lock.Unlock() }() - // setup heights/time + // setup time currentTime := defaultGenesisTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) @@ -1955,7 +1955,7 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { }() subnetID := testSubnet1.TxID - // setup heights/time + // setup time currentTime := defaultGenesisTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) From b1734aa6e7fb288474330242f9e98ac694080186 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 16:56:18 +0200 Subject: [PATCH 19/27] nit --- .../validators_set_property_test.go | 167 ++++++++++-------- 1 file changed, 94 insertions(+), 73 deletions(-) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validators_set_property_test.go index 232b8ee09f3f..9d5eda5f2bd1 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validators_set_property_test.go @@ -86,7 +86,8 @@ func TestGetValidatorsSetProperty(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - // build validator sequences out of pseudo-random events + // build a valid sequence of validators start/end times, given the random + // events sequence received as test input validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { return fmt.Sprintf("failed building events sequence, %s", err.Error()) @@ -116,8 +117,8 @@ func TestGetValidatorsSetProperty(t *testing.T) { } } - switch ev.Priority { - case txs.SubnetPermissionlessValidatorCurrentPriority: + switch ev.eventType { + case startSubnetValidator: currentSubnetValidator, err = addSubnetValidator(vm, ev, subnetID) if err != nil { return fmt.Sprintf("could not add subnet validator, %s", err.Error()) @@ -126,7 +127,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) } - case txs.PrimaryNetworkValidatorCurrentPriority: + case startPrimaryWithoutBLS: // when adding a primary validator, also remove the current primary one if currentPrimaryValidator != nil { err := terminatePrimaryValidator(vm, currentPrimaryValidator) @@ -139,24 +140,37 @@ func TestGetValidatorsSetProperty(t *testing.T) { return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) } } + currentPrimaryValidator, err = addPrimaryValidatorWithoutBLSKey(vm, ev) + if err != nil { + return fmt.Sprintf("could not add primary validator without BLS key, %s", err.Error()) + } + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + } - if ev.PublicKey == nil { - currentPrimaryValidator, err = addPrimaryValidatorWithoutBLSKey(vm, ev) + case startPrimaryWithBLS: + // when adding a primary validator, also remove the current primary one + if currentPrimaryValidator != nil { + err := terminatePrimaryValidator(vm, currentPrimaryValidator) if err != nil { - return fmt.Sprintf("could not create AddValidatorTx, %s", err.Error()) + return fmt.Sprintf("could not terminate current primary validator, %s", err.Error()) } - } else { - currentPrimaryValidator, err = addPrimaryValidatorWithBLSKey(vm, ev) - if err != nil { - return fmt.Sprintf("could not add primary validator with BLS key, %s", err.Error()) + // no need to nil current primary validator, we'll reassign immediately + + if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { + return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) } } + currentPrimaryValidator, err = addPrimaryValidatorWithBLSKey(vm, ev) + if err != nil { + return fmt.Sprintf("could not add primary validator with BLS key, %s", err.Error()) + } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) } default: - return fmt.Sprintf("unexpected staker type, %v", ev.Priority) + return fmt.Sprintf("unexpected staker type, %v", ev.eventType) } } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { @@ -239,13 +253,13 @@ func takeValidatorsSnapshotAtCurrentHeight(vm *VM, validatorsSetByHeightAndSubne return nil } -func addSubnetValidator(vm *VM, data *state.Staker, subnetID ids.ID) (*state.Staker, error) { +func addSubnetValidator(vm *VM, data *validatorInputData, subnetID ids.ID) (*state.Staker, error) { addr := keys[0].PublicKey().Address() signedTx, err := vm.txBuilder.NewAddSubnetValidatorTx( vm.Config.MinValidatorStake, - uint64(data.StartTime.Unix()), - uint64(data.EndTime.Unix()), - data.NodeID, + uint64(data.startTime.Unix()), + uint64(data.endTime.Unix()), + data.nodeID, subnetID, []*secp256k1.PrivateKey{keys[0], keys[1]}, addr, @@ -256,7 +270,7 @@ func addSubnetValidator(vm *VM, data *state.Staker, subnetID ids.ID) (*state.Sta return internalAddValidator(vm, signedTx) } -func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, error) { +func addPrimaryValidatorWithBLSKey(vm *VM, data *validatorInputData) (*state.Staker, error) { addr := keys[0].PublicKey().Address() utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) ins, unstakedOuts, stakedOuts, signers, err := utxoHandler.Spend( @@ -282,9 +296,9 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, e Outs: unstakedOuts, }}, Validator: txs.Validator{ - NodeID: data.NodeID, - Start: uint64(data.StartTime.Unix()), - End: uint64(data.EndTime.Unix()), + NodeID: data.nodeID, + Start: uint64(data.startTime.Unix()), + End: uint64(data.endTime.Unix()), Wght: vm.MinValidatorStake, }, Subnet: constants.PrimaryNetworkID, @@ -316,13 +330,13 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *state.Staker) (*state.Staker, e return internalAddValidator(vm, signedTx) } -func addPrimaryValidatorWithoutBLSKey(vm *VM, data *state.Staker) (*state.Staker, error) { +func addPrimaryValidatorWithoutBLSKey(vm *VM, data *validatorInputData) (*state.Staker, error) { addr := keys[0].PublicKey().Address() signedTx, err := vm.txBuilder.NewAddValidatorTx( vm.Config.MinValidatorStake, - uint64(data.StartTime.Unix()), - uint64(data.EndTime.Unix()), - data.NodeID, + uint64(data.startTime.Unix()), + uint64(data.endTime.Unix()), + data.nodeID, addr, reward.PercentDenominator, []*secp256k1.PrivateKey{keys[0], keys[1]}, @@ -440,11 +454,19 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { return nil } +type validatorInputData struct { + eventType string + startTime time.Time + endTime time.Time + nodeID ids.NodeID + publicKey *bls.PublicKey +} + // buildTimestampsList creates validators start and end time, given the event list. // output is returned as a list of state.Stakers, just because it's a convenient object to // collect all relevant information. -func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.NodeID) ([]*state.Staker, error) { - res := make([]*state.Staker, 0, len(events)) +func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.NodeID) ([]*validatorInputData, error) { + res := make([]*validatorInputData, 0, len(events)) currentTime = currentTime.Add(executor.SyncBound) switch endTime := currentTime.Add(defaultMinStakingDuration); events[0] { @@ -454,20 +476,20 @@ func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.Node return nil, fmt.Errorf("could not make private key, %w", err) } - res = append(res, &state.Staker{ - NodeID: nodeID, - StartTime: currentTime, - EndTime: endTime, - PublicKey: bls.PublicFromSecretKey(sk), - Priority: txs.PrimaryNetworkValidatorCurrentPriority, + res = append(res, &validatorInputData{ + eventType: startPrimaryWithBLS, + startTime: currentTime, + endTime: endTime, + nodeID: nodeID, + publicKey: bls.PublicFromSecretKey(sk), }) case startPrimaryWithoutBLS: - res = append(res, &state.Staker{ - NodeID: nodeID, - StartTime: currentTime, - EndTime: endTime, - PublicKey: nil, - Priority: txs.PrimaryNetworkValidatorCurrentPriority, + res = append(res, &validatorInputData{ + eventType: startPrimaryWithoutBLS, + startTime: currentTime, + endTime: endTime, + nodeID: nodeID, + publicKey: nil, }) default: return nil, fmt.Errorf("unexpected initial event %s", events[0]) @@ -482,45 +504,44 @@ func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.Node switch currentEvent := events[i]; currentEvent { case startSubnetValidator: endTime := currentTime.Add(defaultMinStakingDuration) - val := &state.Staker{ - NodeID: nodeID, - StartTime: currentTime, - EndTime: endTime, - PublicKey: nil, - Priority: txs.SubnetPermissionlessValidatorCurrentPriority, - } - res = append(res, val) - - currentPrimaryVal.EndTime = endTime.Add(time.Second) + res = append(res, &validatorInputData{ + eventType: startSubnetValidator, + startTime: currentTime, + endTime: endTime, + nodeID: nodeID, + publicKey: nil, + }) + + currentPrimaryVal.endTime = endTime.Add(time.Second) currentTime = endTime.Add(time.Second) case startPrimaryWithBLS: - currentTime = currentPrimaryVal.EndTime.Add(executor.SyncBound) + currentTime = currentPrimaryVal.endTime.Add(executor.SyncBound) sk, err := bls.NewSecretKey() if err != nil { return nil, fmt.Errorf("could not make private key, %w", err) } endTime := currentTime.Add(defaultMinStakingDuration) - val := &state.Staker{ - NodeID: nodeID, - StartTime: currentTime, - EndTime: endTime, - PublicKey: bls.PublicFromSecretKey(sk), - Priority: txs.PrimaryNetworkValidatorCurrentPriority, + val := &validatorInputData{ + eventType: startPrimaryWithBLS, + startTime: currentTime, + endTime: endTime, + nodeID: nodeID, + publicKey: bls.PublicFromSecretKey(sk), } res = append(res, val) currentPrimaryVal = val case startPrimaryWithoutBLS: - currentTime = currentPrimaryVal.EndTime.Add(executor.SyncBound) + currentTime = currentPrimaryVal.endTime.Add(executor.SyncBound) endTime := currentTime.Add(defaultMinStakingDuration) - val := &state.Staker{ - NodeID: nodeID, - StartTime: currentTime, - EndTime: endTime, - PublicKey: nil, - Priority: txs.PrimaryNetworkValidatorCurrentPriority, + val := &validatorInputData{ + eventType: startPrimaryWithoutBLS, + startTime: currentTime, + endTime: endTime, + nodeID: nodeID, + publicKey: nil, } res = append(res, val) currentPrimaryVal = val @@ -548,7 +569,7 @@ func TestTimestampListGenerator(t *testing.T) { // nil out non subnet validators subnetIndexes := make([]int, 0) for idx, ev := range validatorsTimes { - if ev.Priority == txs.SubnetPermissionlessValidatorCurrentPriority { + if ev.eventType == startSubnetValidator { subnetIndexes = append(subnetIndexes, idx) } } @@ -561,15 +582,15 @@ func TestTimestampListGenerator(t *testing.T) { if ev == nil { continue // a subnet validator } - if currentEventTime.After(ev.StartTime) { + if currentEventTime.After(ev.startTime) { return fmt.Sprintf("validator %d start time larger than current event time", i) } - if ev.StartTime.After(ev.EndTime) { + if ev.startTime.After(ev.endTime) { return fmt.Sprintf("validator %d start time larger than its end time", i) } - currentEventTime = ev.EndTime + currentEventTime = ev.endTime } return "" @@ -601,7 +622,7 @@ func TestTimestampListGenerator(t *testing.T) { // nil out non subnet validators nonSubnetIndexes := make([]int, 0) for idx, ev := range validatorsTimes { - if ev.Priority != txs.SubnetPermissionlessValidatorCurrentPriority { + if ev.eventType != startSubnetValidator { nonSubnetIndexes = append(nonSubnetIndexes, idx) } } @@ -614,15 +635,15 @@ func TestTimestampListGenerator(t *testing.T) { if ev == nil { continue // a non-subnet validator } - if currentEventTime.After(ev.StartTime) { + if currentEventTime.After(ev.startTime) { return fmt.Sprintf("validator %d start time larger than current event time", i) } - if ev.StartTime.After(ev.EndTime) { + if ev.startTime.After(ev.endTime) { return fmt.Sprintf("validator %d start time larger than its end time", i) } - currentEventTime = ev.EndTime + currentEventTime = ev.endTime } return "" @@ -653,14 +674,14 @@ func TestTimestampListGenerator(t *testing.T) { currentPrimaryValidator := validatorsTimes[0] for i := 1; i < len(validatorsTimes); i++ { - if validatorsTimes[i].Priority != txs.SubnetPermissionlessValidatorCurrentPriority { + if validatorsTimes[i].eventType != startSubnetValidator { currentPrimaryValidator = validatorsTimes[i] continue } subnetVal := validatorsTimes[i] - if currentPrimaryValidator.StartTime.After(subnetVal.StartTime) || - subnetVal.EndTime.After(currentPrimaryValidator.EndTime) { + if currentPrimaryValidator.startTime.After(subnetVal.startTime) || + subnetVal.endTime.After(currentPrimaryValidator.endTime) { return "subnet validator not bounded by primary network ones" } } From f6d9ce9df44189613bc563b34e35279d3bb6b51c Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 21:00:16 +0200 Subject: [PATCH 20/27] nits --- ...test.go => validator_set_property_test.go} | 28 +++++++++---------- vms/platformvm/validators/manager.go | 14 ++++++---- vms/platformvm/vm_regression_test.go | 3 +- 3 files changed, 25 insertions(+), 20 deletions(-) rename vms/platformvm/{validators_set_property_test.go => validator_set_property_test.go} (97%) diff --git a/vms/platformvm/validators_set_property_test.go b/vms/platformvm/validator_set_property_test.go similarity index 97% rename from vms/platformvm/validators_set_property_test.go rename to vms/platformvm/validator_set_property_test.go index 9d5eda5f2bd1..6da812c97239 100644 --- a/vms/platformvm/validators_set_property_test.go +++ b/vms/platformvm/validator_set_property_test.go @@ -50,9 +50,9 @@ import ( ) const ( - startPrimaryWithBLS = "primary-validator-with-BLS-key\n" - startPrimaryWithoutBLS = "primary-validator-without-BLS-key\n" - startSubnetValidator = "subnet-validator\n" + startPrimaryWithBLS uint8 = 0 + startPrimaryWithoutBLS uint8 = 1 + startSubnetValidator uint8 = 2 ) var errEmptyEventsList = errors.New("empty events list") @@ -70,7 +70,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { // properties := gopter.NewProperties(parameters) properties.Property("check GetValidatorSet", prop.ForAll( - func(events []string) string { + func(events []uint8) string { vm, subnetID, err := buildVM() if err != nil { return fmt.Sprintf("failed building vm, %s", err.Error()) @@ -196,7 +196,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { startPrimaryWithoutBLS, startSubnetValidator, )).SuchThat(func(v interface{}) bool { - list := v.([]string) + list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), ), @@ -455,7 +455,7 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { } type validatorInputData struct { - eventType string + eventType uint8 startTime time.Time endTime time.Time nodeID ids.NodeID @@ -465,7 +465,7 @@ type validatorInputData struct { // buildTimestampsList creates validators start and end time, given the event list. // output is returned as a list of state.Stakers, just because it's a convenient object to // collect all relevant information. -func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.NodeID) ([]*validatorInputData, error) { +func buildTimestampsList(events []uint8, currentTime time.Time, nodeID ids.NodeID) ([]*validatorInputData, error) { res := make([]*validatorInputData, 0, len(events)) currentTime = currentTime.Add(executor.SyncBound) @@ -492,7 +492,7 @@ func buildTimestampsList(events []string, currentTime time.Time, nodeID ids.Node publicKey: nil, }) default: - return nil, fmt.Errorf("unexpected initial event %s", events[0]) + return nil, fmt.Errorf("unexpected initial event %d", events[0]) } // track current primary validator to make sure its staking period @@ -554,7 +554,7 @@ func TestTimestampListGenerator(t *testing.T) { properties := gopter.NewProperties(nil) properties.Property("primary validators are returned in sequence", prop.ForAll( - func(events []string) string { + func(events []uint8) string { currentTime := time.Now() nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) @@ -600,14 +600,14 @@ func TestTimestampListGenerator(t *testing.T) { startPrimaryWithoutBLS, startSubnetValidator, )).SuchThat(func(v interface{}) bool { - list := v.([]string) + list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), ), ) properties.Property("subnet validators are returned in sequence", prop.ForAll( - func(events []string) string { + func(events []uint8) string { currentTime := time.Now() nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) @@ -653,14 +653,14 @@ func TestTimestampListGenerator(t *testing.T) { startPrimaryWithoutBLS, startSubnetValidator, )).SuchThat(func(v interface{}) bool { - list := v.([]string) + list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), ), ) properties.Property("subnet validators' times are bound by a primary validator's times", prop.ForAll( - func(events []string) string { + func(events []uint8) string { currentTime := time.Now() nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) @@ -692,7 +692,7 @@ func TestTimestampListGenerator(t *testing.T) { startPrimaryWithoutBLS, startSubnetValidator, )).SuchThat(func(v interface{}) bool { - list := v.([]string) + list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), ), diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index bf78a05cdc7e..ac6c0743ea70 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -165,6 +165,8 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } } currentSubnetValidatorList := currentSubnetValidators.List() + + // Node ID --> Information about the node's validating of [subnetID]. subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) for _, vdr := range currentSubnetValidatorList { subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ @@ -180,6 +182,8 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return nil, ErrMissingValidator } currentPrimaryValidatorList := currentPrimaryNetworkValidators.List() + + // Node ID --> Validator information for the node validating the Primary Network. primarySet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentPrimaryValidatorList)) for _, vdr := range currentPrimaryValidatorList { primarySet[vdr.NodeID] = &validators.GetValidatorOutput{ @@ -189,8 +193,8 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } // fill PK from primary network - if _, found := subnetSet[vdr.NodeID]; found { - subnetSet[vdr.NodeID].PublicKey = vdr.PublicKey + if subnetVdr, found := subnetSet[vdr.NodeID]; found { + subnetVdr.PublicKey = vdr.PublicKey } } @@ -211,7 +215,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } func (m *manager) applyValidatorDiffs( - targetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, + subnetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, subnetID ids.ID, height uint64, ) error { @@ -244,13 +248,13 @@ func (m *manager) applyValidatorDiffs( return err } for nodeID, weightDiff := range targetWeightDiffs { - if err := applyWeightDiff(targetSet, nodeID, weightDiff); err != nil { + if err := applyWeightDiff(subnetSet, nodeID, weightDiff); err != nil { return err } } // rebuild public key of target validators, just peeking in primary validators set - for nodeID, vdr := range targetSet { + for nodeID, vdr := range subnetSet { if primary, found := primarySet[nodeID]; found { vdr.PublicKey = primary.PublicKey } diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index 48add26f44f7..e07f877a49ae 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -32,7 +32,6 @@ import ( "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" - blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" "github.com/ava-labs/avalanchego/vms/platformvm/config" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/reward" @@ -42,6 +41,8 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" "github.com/ava-labs/avalanchego/vms/platformvm/utxo" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" ) func TestAddDelegatorTxOverDelegatedRegression(t *testing.T) { From efbbf20041c82e31baec5a637fdbea5f1889a2ca Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 6 Jun 2023 21:22:30 +0200 Subject: [PATCH 21/27] cleanup --- vms/platformvm/validators/manager.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index ac6c0743ea70..f7aca8e684af 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -198,8 +198,15 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } } + // fully rebuild primary network validators at [height] first for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { - if err := m.applyValidatorDiffs(subnetSet, primarySet, subnetID, diffHeight); err != nil { + if err := m.rebuildPrimaryValidatorSet(primarySet, diffHeight); err != nil { + return nil, err + } + } + + for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { + if err := m.rebuildSubnetValidatorSet(subnetSet, primarySet, subnetID, diffHeight); err != nil { return nil, err } } @@ -214,12 +221,10 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return subnetSet, nil } -func (m *manager) applyValidatorDiffs( - subnetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, - subnetID ids.ID, +func (m *manager) rebuildPrimaryValidatorSet( + primarySet map[ids.NodeID]*validators.GetValidatorOutput, height uint64, ) error { - // fully rebuild primary network validators at [height] primaryWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, constants.PlatformChainID) if err != nil { return err @@ -241,8 +246,14 @@ func (m *manager) applyValidatorDiffs( vdr.PublicKey = pk } } + return nil +} - // rebuild weights of target validators +func (m *manager) rebuildSubnetValidatorSet( + subnetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, + subnetID ids.ID, + height uint64, +) error { targetWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, subnetID) if err != nil { return err From b0e038ff7af4b0e968e26016d5c41dfefe98a642 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Wed, 7 Jun 2023 08:56:33 +0200 Subject: [PATCH 22/27] nit --- vms/platformvm/vm_regression_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index e07f877a49ae..128f1426c4ed 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -5,7 +5,6 @@ package platformvm import ( "context" - "encoding/hex" "errors" "testing" "time" @@ -1480,11 +1479,11 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { ) // insert primary network validator - nodeID := ids.GenerateTestNodeID() - addr := keys[0].PublicKey().Address() - skBytes, err := hex.DecodeString("6668fecd4595b81e4d568398c820bbf3f073cb222902279fa55ebb84764ed2e3") - require.NoError(err) - sk1, err := bls.SecretKeyFromBytes(skBytes) + var ( + nodeID = ids.GenerateTestNodeID() + addr = keys[0].PublicKey().Address() + ) + sk1, err := bls.NewSecretKey() require.NoError(err) // build primary network validator with BLS key From 6540524a712eb5e14d52b0299498cd2a51bf4cd0 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Wed, 7 Jun 2023 14:39:32 +0200 Subject: [PATCH 23/27] nit --- vms/platformvm/vm_regression_test.go | 141 ++++++++------------------- 1 file changed, 38 insertions(+), 103 deletions(-) diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index 128f1426c4ed..1d77e9429204 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -1534,22 +1534,13 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { require.NoError(primaryTx.SyntacticVerify(vm.ctx)) require.NoError(vm.Builder.AddUnverifiedTx(primaryTx)) - blk, err := vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting primary validator to current currentTime = primaryStartTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -1570,22 +1561,13 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { require.NoError(err) require.NoError(vm.Builder.AddUnverifiedTx(subnetTx)) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting the subnet validator to current currentTime = subnetStartTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(subnetID, nodeID) require.NoError(err) @@ -1597,12 +1579,7 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { currentTime = subnetEndTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(subnetID, nodeID) require.ErrorIs(err, database.ErrNotFound) @@ -1615,7 +1592,7 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator require.NoError(err) require.NoError(blk.Verify(context.Background())) @@ -1688,22 +1665,13 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting restarted primary validator to current currentTime = primaryReStartTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -1805,22 +1773,13 @@ func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { require.NoError(err) require.NoError(vm.Builder.AddUnverifiedTx(primaryTx1)) - blk, err := vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting primary validator to current currentTime = primaryStartTime1 vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -1833,7 +1792,7 @@ func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator require.NoError(err) require.NoError(blk.Verify(context.Background())) @@ -1906,22 +1865,13 @@ func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting restarted primary validator to current currentTime = primaryStartTime2 vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -1986,22 +1936,13 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { require.NoError(err) require.NoError(vm.Builder.AddUnverifiedTx(primaryTx1)) - blk, err := vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting primary validator to current currentTime = primaryStartTime1 vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -2022,22 +1963,13 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { require.NoError(err) require.NoError(vm.Builder.AddUnverifiedTx(subnetTx)) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting the subnet validator to current currentTime = subnetStartTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(subnetID, nodeID) require.NoError(err) @@ -2049,12 +1981,7 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { currentTime = subnetEndTime vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(subnetID, nodeID) require.ErrorIs(err, database.ErrNotFound) @@ -2067,7 +1994,7 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - blk, err = vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator + blk, err := vm.Builder.BuildBlock(context.Background()) // must be a proposal block rewarding the primary validator require.NoError(err) require.NoError(blk.Verify(context.Background())) @@ -2140,23 +2067,14 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { require.NoError(uPrimaryRestartTx.SyntacticVerify(vm.ctx)) require.NoError(vm.Builder.AddUnverifiedTx(primaryRestartTx)) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) + require.NoError(buildAndAcceptStandardBlock(vm)) // move time ahead, promoting restarted primary validator to current currentTime = primaryStartTime2 vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - blk, err = vm.Builder.BuildBlock(context.Background()) - require.NoError(err) - require.NoError(blk.Verify(context.Background())) - require.NoError(blk.Accept(context.Background())) - require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) - + require.NoError(buildAndAcceptStandardBlock(vm)) _, err = vm.state.GetCurrentValidator(constants.PrimaryNetworkID, nodeID) require.NoError(err) @@ -2181,6 +2099,23 @@ func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { } } +func buildAndAcceptStandardBlock(vm *VM) error { + blk, err := vm.Builder.BuildBlock(context.Background()) + if err != nil { + return err + } + + if err := blk.Verify(context.Background()); err != nil { + return err + } + + if err := blk.Accept(context.Background()); err != nil { + return err + } + + return vm.SetPreference(context.Background(), vm.manager.LastAccepted()) +} + func checkValidatorBlsKeyIsSet( valState validators.State, nodeID ids.NodeID, From 6b4900686c0a7994685da548481c21ff09b6feb7 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 8 Jun 2023 02:37:34 -0400 Subject: [PATCH 24/27] Pchain bls diffs fix cleanup (#1594) --- vms/platformvm/validator_set_property_test.go | 12 +- vms/platformvm/validators/manager.go | 209 ++++++++++-------- 2 files changed, 123 insertions(+), 98 deletions(-) diff --git a/vms/platformvm/validator_set_property_test.go b/vms/platformvm/validator_set_property_test.go index 6da812c97239..05b5c8538776 100644 --- a/vms/platformvm/validator_set_property_test.go +++ b/vms/platformvm/validator_set_property_test.go @@ -199,8 +199,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), - ), - ) + )) properties.TestingRun(t) } @@ -603,8 +602,7 @@ func TestTimestampListGenerator(t *testing.T) { list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), - ), - ) + )) properties.Property("subnet validators are returned in sequence", prop.ForAll( func(events []uint8) string { @@ -656,8 +654,7 @@ func TestTimestampListGenerator(t *testing.T) { list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), - ), - ) + )) properties.Property("subnet validators' times are bound by a primary validator's times", prop.ForAll( func(events []uint8) string { @@ -695,8 +692,7 @@ func TestTimestampListGenerator(t *testing.T) { list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), - ), - ) + )) properties.TestingRun(t) } diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index f7aca8e684af..6105839869a2 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -131,7 +131,31 @@ func (m *manager) GetCurrentHeight(context.Context) (uint64, error) { return lastAccepted.Height(), nil } -func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { +func (m *manager) GetValidatorSet( + ctx context.Context, + height uint64, + subnetID ids.ID, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + lastAcceptedHeight, err := m.GetCurrentHeight(ctx) + if err != nil { + return nil, err + } + if lastAcceptedHeight < height { + return nil, database.ErrNotFound + } + + return m.getValidatorSetFrom(lastAcceptedHeight, height, subnetID) +} + +// getValidatorSetFrom fetches the validator set of [subnetID] at [targetHeight] +// or builds it starting from [currentHeight]. +// +// Invariant: [m.cfg.Validators] contains the validator set at [currentHeight]. +func (m *manager) getValidatorSetFrom( + currentHeight uint64, + targetHeight uint64, + subnetID ids.ID, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { validatorSetsCache, exists := m.caches[subnetID] if !exists { validatorSetsCache = &cache.LRU[uint64, map[ids.NodeID]*validators.GetValidatorOutput]{Size: validatorSetsCacheSize} @@ -141,137 +165,142 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } } - if validatorSet, ok := validatorSetsCache.Get(height); ok { + if validatorSet, ok := validatorSetsCache.Get(targetHeight); ok { m.metrics.IncValidatorSetsCached() return validatorSet, nil } - lastAcceptedHeight, err := m.GetCurrentHeight(ctx) - if err != nil { - return nil, err - } - if lastAcceptedHeight < height { - return nil, database.ErrNotFound - } - // get the start time to track metrics startTime := m.clk.Time() - currentSubnetValidators, ok := m.cfg.Validators.Get(subnetID) - if !ok { - currentSubnetValidators = validators.NewSet() - if err := m.state.ValidatorSet(subnetID, currentSubnetValidators); err != nil { - return nil, err - } + var ( + validatorSet map[ids.NodeID]*validators.GetValidatorOutput + err error + ) + if subnetID == constants.PrimaryNetworkID { + validatorSet, err = m.makePrimaryNetworkValidatorSet(currentHeight, targetHeight) + } else { + validatorSet, err = m.makeSubnetValidatorSet(currentHeight, targetHeight, subnetID) } - currentSubnetValidatorList := currentSubnetValidators.List() - - // Node ID --> Information about the node's validating of [subnetID]. - subnetSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentSubnetValidatorList)) - for _, vdr := range currentSubnetValidatorList { - subnetSet[vdr.NodeID] = &validators.GetValidatorOutput{ - NodeID: vdr.NodeID, - // PublicKey will be picked from primary validators - Weight: vdr.Weight, - } + if err != nil { + return nil, err } - currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) + // cache the validator set + validatorSetsCache.Put(targetHeight, validatorSet) + + endTime := m.clk.Time() + m.metrics.IncValidatorSetsCreated() + m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) + m.metrics.AddValidatorSetsHeightDiff(currentHeight - targetHeight) + return validatorSet, nil +} + +func (m *manager) makePrimaryNetworkValidatorSet( + currentHeight uint64, + targetHeight uint64, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + currentValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { // This should never happen return nil, ErrMissingValidator } - currentPrimaryValidatorList := currentPrimaryNetworkValidators.List() + currentValidatorList := currentValidators.List() - // Node ID --> Validator information for the node validating the Primary Network. - primarySet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentPrimaryValidatorList)) - for _, vdr := range currentPrimaryValidatorList { - primarySet[vdr.NodeID] = &validators.GetValidatorOutput{ + // Node ID --> Validator information for the node validating the Primary + // Network. + validatorSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentValidatorList)) + for _, vdr := range currentValidatorList { + validatorSet[vdr.NodeID] = &validators.GetValidatorOutput{ NodeID: vdr.NodeID, PublicKey: vdr.PublicKey, Weight: vdr.Weight, } - - // fill PK from primary network - if subnetVdr, found := subnetSet[vdr.NodeID]; found { - subnetVdr.PublicKey = vdr.PublicKey - } } - // fully rebuild primary network validators at [height] first - for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { - if err := m.rebuildPrimaryValidatorSet(primarySet, diffHeight); err != nil { + // Rebuild primary network validators at [height] + for diffHeight := currentHeight; diffHeight > targetHeight; diffHeight-- { + weightDiffs, err := m.state.GetValidatorWeightDiffs(diffHeight, constants.PlatformChainID) + if err != nil { return nil, err } - } + for nodeID, weightDiff := range weightDiffs { + if err := applyWeightDiff(validatorSet, nodeID, weightDiff); err != nil { + return nil, err + } + } - for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { - if err := m.rebuildSubnetValidatorSet(subnetSet, primarySet, subnetID, diffHeight); err != nil { + pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(diffHeight) + if err != nil { return nil, err } + for nodeID, pk := range pkDiffs { + if vdr, ok := validatorSet[nodeID]; ok { + // The validator's public key was removed at this block, so it + // was in the validator set before. + vdr.PublicKey = pk + } + } } - - // cache the validator set - validatorSetsCache.Put(height, subnetSet) - - endTime := m.clk.Time() - m.metrics.IncValidatorSetsCreated() - m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) - m.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) - return subnetSet, nil + return validatorSet, nil } -func (m *manager) rebuildPrimaryValidatorSet( - primarySet map[ids.NodeID]*validators.GetValidatorOutput, - height uint64, -) error { - primaryWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, constants.PlatformChainID) - if err != nil { - return err - } - for nodeID, weightDiff := range primaryWeightDiffs { - if err := applyWeightDiff(primarySet, nodeID, weightDiff); err != nil { - return err +func (m *manager) makeSubnetValidatorSet( + currentHeight uint64, + targetHeight uint64, + subnetID ids.ID, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + currentValidators, ok := m.cfg.Validators.Get(subnetID) + if !ok { + currentValidators = validators.NewSet() + if err := m.state.ValidatorSet(subnetID, currentValidators); err != nil { + return nil, err } } + currentValidatorList := currentValidators.List() - pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(height) - if err != nil { - return err + // Node ID --> Validator information for the node validating the Subnet. + subnetValidatorSet := make(map[ids.NodeID]*validators.GetValidatorOutput, len(currentValidatorList)) + for _, vdr := range currentValidatorList { + subnetValidatorSet[vdr.NodeID] = &validators.GetValidatorOutput{ + NodeID: vdr.NodeID, + // PublicKey will be picked from primary validators + Weight: vdr.Weight, + } } - for nodeID, pk := range pkDiffs { - if vdr, ok := primarySet[nodeID]; ok { - // The validator's public key was removed at this block, so it - // was in the validator set before. - vdr.PublicKey = pk + + // Rebuild subnet validators at [targetHeight] + for diffHeight := currentHeight; diffHeight > targetHeight; diffHeight-- { + weightDiffs, err := m.state.GetValidatorWeightDiffs(diffHeight, subnetID) + if err != nil { + return nil, err + } + + for nodeID, weightDiff := range weightDiffs { + if err := applyWeightDiff(subnetValidatorSet, nodeID, weightDiff); err != nil { + return nil, err + } } } - return nil -} -func (m *manager) rebuildSubnetValidatorSet( - subnetSet, primarySet map[ids.NodeID]*validators.GetValidatorOutput, - subnetID ids.ID, - height uint64, -) error { - targetWeightDiffs, err := m.state.GetValidatorWeightDiffs(height, subnetID) + // Get the public keys for all the validators at [targetHeight] + primarySet, err := m.getValidatorSetFrom(currentHeight, targetHeight, constants.PrimaryNetworkID) if err != nil { - return err - } - for nodeID, weightDiff := range targetWeightDiffs { - if err := applyWeightDiff(subnetSet, nodeID, weightDiff); err != nil { - return err - } + return nil, err } - // rebuild public key of target validators, just peeking in primary validators set - for nodeID, vdr := range subnetSet { - if primary, found := primarySet[nodeID]; found { - vdr.PublicKey = primary.PublicKey + // Update the subnet validator set to include the public keys at + // [targetHeight]. + for nodeID, subnetValidator := range subnetValidatorSet { + primaryValidator, ok := primarySet[nodeID] + if !ok { + // This should never happen + return nil, ErrMissingValidator } + subnetValidator.PublicKey = primaryValidator.PublicKey } - return nil + return subnetValidatorSet, nil } func applyWeightDiff( From 6286ca72fe6513423ae2d11847d058b4346a5725 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 8 Jun 2023 11:51:57 -0400 Subject: [PATCH 25/27] format nits --- vms/platformvm/validator_set_property_test.go | 154 +++++++++--------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/vms/platformvm/validator_set_property_test.go b/vms/platformvm/validator_set_property_test.go index 05b5c8538776..ef1297cd9a99 100644 --- a/vms/platformvm/validator_set_property_test.go +++ b/vms/platformvm/validator_set_property_test.go @@ -50,18 +50,18 @@ import ( ) const ( - startPrimaryWithBLS uint8 = 0 - startPrimaryWithoutBLS uint8 = 1 - startSubnetValidator uint8 = 2 + startPrimaryWithBLS uint8 = iota + startPrimaryWithoutBLS + startSubnetValidator ) var errEmptyEventsList = errors.New("empty events list") -// for a given (permissioned) subnet, the test stakes and restakes multiple times -// a node as a primary and subnet validator. The BLS key of the node is changed across -// staking periods, and it can even be nil. We test that GetValidatorSet returns -// the correct primary and subnet validators data, with the right BLS key version at -// all relevant heights. +// for a given (permissioned) subnet, the test stakes and restakes multiple +// times a node as a primary and subnet validator. The BLS key of the node is +// changed across staking periods, and it can even be nil. We test that +// GetValidatorSet returns the correct primary and subnet validators data, with +// the right BLS key version at all relevant heights. func TestGetValidatorsSetProperty(t *testing.T) { properties := gopter.NewProperties(nil) @@ -73,7 +73,7 @@ func TestGetValidatorsSetProperty(t *testing.T) { func(events []uint8) string { vm, subnetID, err := buildVM() if err != nil { - return fmt.Sprintf("failed building vm, %s", err.Error()) + return fmt.Sprintf("failed building vm: %s", err.Error()) } vm.ctx.Lock.Lock() defer func() { @@ -86,16 +86,16 @@ func TestGetValidatorsSetProperty(t *testing.T) { vm.clock.Set(currentTime) vm.state.SetTimestamp(currentTime) - // build a valid sequence of validators start/end times, given the random - // events sequence received as test input + // build a valid sequence of validators start/end times, given the + // random events sequence received as test input validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { - return fmt.Sprintf("failed building events sequence, %s", err.Error()) + return fmt.Sprintf("failed building events sequence: %s", err.Error()) } validatorsSetByHeightAndSubnet := make(map[uint64]map[ids.ID]map[ids.NodeID]*validators.GetValidatorOutput) if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } // insert validator sequence @@ -108,12 +108,12 @@ func TestGetValidatorsSetProperty(t *testing.T) { if currentSubnetValidator != nil { err := terminateSubnetValidator(vm, currentSubnetValidator) if err != nil { - return fmt.Sprintf("could not terminate current subnet validator, %s", err.Error()) + return fmt.Sprintf("could not terminate current subnet validator: %s", err.Error()) } currentSubnetValidator = nil if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } } @@ -121,60 +121,64 @@ func TestGetValidatorsSetProperty(t *testing.T) { case startSubnetValidator: currentSubnetValidator, err = addSubnetValidator(vm, ev, subnetID) if err != nil { - return fmt.Sprintf("could not add subnet validator, %s", err.Error()) + return fmt.Sprintf("could not add subnet validator: %s", err.Error()) } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } case startPrimaryWithoutBLS: - // when adding a primary validator, also remove the current primary one + // when adding a primary validator, also remove the current + // primary one if currentPrimaryValidator != nil { err := terminatePrimaryValidator(vm, currentPrimaryValidator) if err != nil { - return fmt.Sprintf("could not terminate current primary validator, %s", err.Error()) + return fmt.Sprintf("could not terminate current primary validator: %s", err.Error()) } - // no need to nil current primary validator, we'll reassign immediately + // no need to nil current primary validator, we'll + // reassign immediately if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } } currentPrimaryValidator, err = addPrimaryValidatorWithoutBLSKey(vm, ev) if err != nil { - return fmt.Sprintf("could not add primary validator without BLS key, %s", err.Error()) + return fmt.Sprintf("could not add primary validator without BLS key: %s", err.Error()) } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } case startPrimaryWithBLS: - // when adding a primary validator, also remove the current primary one + // when adding a primary validator, also remove the current + // primary one if currentPrimaryValidator != nil { err := terminatePrimaryValidator(vm, currentPrimaryValidator) if err != nil { - return fmt.Sprintf("could not terminate current primary validator, %s", err.Error()) + return fmt.Sprintf("could not terminate current primary validator: %s", err.Error()) } - // no need to nil current primary validator, we'll reassign immediately + // no need to nil current primary validator, we'll + // reassign immediately if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } } currentPrimaryValidator, err = addPrimaryValidatorWithBLSKey(vm, ev) if err != nil { - return fmt.Sprintf("could not add primary validator with BLS key, %s", err.Error()) + return fmt.Sprintf("could not add primary validator with BLS key: %s", err.Error()) } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } default: - return fmt.Sprintf("unexpected staker type, %v", ev.eventType) + return fmt.Sprintf("unexpected staker type: %v", ev.eventType) } } if err := takeValidatorsSnapshotAtCurrentHeight(vm, validatorsSetByHeightAndSubnet); err != nil { - return fmt.Sprintf("could not take validators snapshot, %s", err.Error()) + return fmt.Sprintf("could not take validators snapshot: %s", err.Error()) } // finally test validators set @@ -182,20 +186,23 @@ func TestGetValidatorsSetProperty(t *testing.T) { for subnet, validatorsSet := range subnetSets { res, err := vm.GetValidatorSet(context.Background(), height, subnet) if err != nil { - return fmt.Sprintf("failed GetValidatorSet, %s", err.Error()) + return fmt.Sprintf("failed GetValidatorSet: %s", err.Error()) } if !reflect.DeepEqual(validatorsSet, res) { - return fmt.Sprintf("failed validators set comparison, %s", err.Error()) + return fmt.Sprintf("failed validators set comparison: %s", err.Error()) } } } return "" }, - gen.SliceOfN(10, gen.OneConstOf( - startPrimaryWithBLS, - startPrimaryWithoutBLS, - startSubnetValidator, - )).SuchThat(func(v interface{}) bool { + gen.SliceOfN( + 10, + gen.OneConstOf( + startPrimaryWithBLS, + startPrimaryWithoutBLS, + startSubnetValidator, + ), + ).SuchThat(func(v interface{}) bool { list := v.([]uint8) return len(list) > 0 && (list[0] == startPrimaryWithBLS || list[0] == startPrimaryWithoutBLS) }), @@ -264,7 +271,7 @@ func addSubnetValidator(vm *VM, data *validatorInputData, subnetID ids.ID) (*sta addr, ) if err != nil { - return nil, fmt.Errorf("could not create AddSubnetValidatorTx, %w", err) + return nil, fmt.Errorf("could not create AddSubnetValidatorTx: %w", err) } return internalAddValidator(vm, signedTx) } @@ -280,11 +287,11 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *validatorInputData) (*state.Sta addr, // change Addresss ) if err != nil { - return nil, fmt.Errorf("could not create inputs/outputs for permissionless validator, %w", err) + return nil, fmt.Errorf("could not create inputs/outputs for permissionless validator: %w", err) } sk, err := bls.NewSecretKey() if err != nil { - return nil, fmt.Errorf("could not create secret key, %w", err) + return nil, fmt.Errorf("could not create secret key: %w", err) } uPrimaryTx := &txs.AddPermissionlessValidatorTx{ @@ -321,10 +328,10 @@ func addPrimaryValidatorWithBLSKey(vm *VM, data *validatorInputData) (*state.Sta } signedTx, err := txs.NewSigned(uPrimaryTx, txs.Codec, signers) if err != nil { - return nil, fmt.Errorf("could not create AddPermissionlessValidatorTx with BLS key, %w", err) + return nil, fmt.Errorf("could not create AddPermissionlessValidatorTx with BLS key: %w", err) } if err := signedTx.SyntacticVerify(vm.ctx); err != nil { - return nil, fmt.Errorf("failed syntax verification of AddPermissionlessValidatorTx, %w", err) + return nil, fmt.Errorf("failed syntax verification of AddPermissionlessValidatorTx: %w", err) } return internalAddValidator(vm, signedTx) } @@ -342,7 +349,7 @@ func addPrimaryValidatorWithoutBLSKey(vm *VM, data *validatorInputData) (*state. addr, ) if err != nil { - return nil, fmt.Errorf("could not create AddValidatorTx, %w", err) + return nil, fmt.Errorf("could not create AddValidatorTx: %w", err) } return internalAddValidator(vm, signedTx) } @@ -350,21 +357,21 @@ func addPrimaryValidatorWithoutBLSKey(vm *VM, data *validatorInputData) (*state. func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { stakerTx := signedTx.Unsigned.(txs.StakerTx) if err := vm.Builder.AddUnverifiedTx(signedTx); err != nil { - return nil, fmt.Errorf("could not add tx to mempool, %w", err) + return nil, fmt.Errorf("could not add tx to mempool: %w", err) } blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return nil, fmt.Errorf("failed building block, %w", err) + return nil, fmt.Errorf("failed building block: %w", err) } if err := blk.Verify(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %w", err) + return nil, fmt.Errorf("failed verifying block: %w", err) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed accepting block, %w", err) + return nil, fmt.Errorf("failed accepting block: %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed setting preference, %w", err) + return nil, fmt.Errorf("failed setting preference: %w", err) } // move time ahead, promoting the validator to current @@ -374,16 +381,16 @@ func internalAddValidator(vm *VM, signedTx *txs.Tx) (*state.Staker, error) { blk, err = vm.Builder.BuildBlock(context.Background()) if err != nil { - return nil, fmt.Errorf("failed building block, %w", err) + return nil, fmt.Errorf("failed building block: %w", err) } if err := blk.Verify(context.Background()); err != nil { - return nil, fmt.Errorf("failed verifying block, %w", err) + return nil, fmt.Errorf("failed verifying block: %w", err) } if err := blk.Accept(context.Background()); err != nil { - return nil, fmt.Errorf("failed accepting block, %w", err) + return nil, fmt.Errorf("failed accepting block: %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return nil, fmt.Errorf("failed setting preference, %w", err) + return nil, fmt.Errorf("failed setting preference: %w", err) } return vm.state.GetCurrentValidator(stakerTx.SubnetID(), stakerTx.NodeID()) @@ -396,16 +403,16 @@ func terminateSubnetValidator(vm *VM, validator *state.Staker) error { blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return fmt.Errorf("failed building block, %w", err) + return fmt.Errorf("failed building block: %w", err) } if err := blk.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying block, %w", err) + return fmt.Errorf("failed verifying block: %w", err) } if err := blk.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting block, %w", err) + return fmt.Errorf("failed accepting block: %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed setting preference, %w", err) + return fmt.Errorf("failed setting preference: %w", err) } return nil @@ -418,36 +425,36 @@ func terminatePrimaryValidator(vm *VM, validator *state.Staker) error { blk, err := vm.Builder.BuildBlock(context.Background()) if err != nil { - return fmt.Errorf("failed building block, %w", err) + return fmt.Errorf("failed building block: %w", err) } if err := blk.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying block, %w", err) + return fmt.Errorf("failed verifying block: %w", err) } proposalBlk := blk.(snowman.OracleBlock) options, err := proposalBlk.Options(context.Background()) if err != nil { - return fmt.Errorf("failed retrieving options, %w", err) + return fmt.Errorf("failed retrieving options: %w", err) } commit := options[0].(*blockexecutor.Block) _, ok := commit.Block.(*blocks.BanffCommitBlock) if !ok { - return fmt.Errorf("failed retrieving commit option, %w", err) + return fmt.Errorf("failed retrieving commit option: %w", err) } if err := blk.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting block, %w", err) + return fmt.Errorf("failed accepting block: %w", err) } if err := commit.Verify(context.Background()); err != nil { - return fmt.Errorf("failed verifying commit block, %w", err) + return fmt.Errorf("failed verifying commit block: %w", err) } if err := commit.Accept(context.Background()); err != nil { - return fmt.Errorf("failed accepting commit block, %w", err) + return fmt.Errorf("failed accepting commit block: %w", err) } if err := vm.SetPreference(context.Background(), vm.manager.LastAccepted()); err != nil { - return fmt.Errorf("failed setting preference, %w", err) + return fmt.Errorf("failed setting preference: %w", err) } return nil @@ -472,7 +479,7 @@ func buildTimestampsList(events []uint8, currentTime time.Time, nodeID ids.NodeI case startPrimaryWithBLS: sk, err := bls.NewSecretKey() if err != nil { - return nil, fmt.Errorf("could not make private key, %w", err) + return nil, fmt.Errorf("could not make private key: %w", err) } res = append(res, &validatorInputData{ @@ -518,7 +525,7 @@ func buildTimestampsList(events []uint8, currentTime time.Time, nodeID ids.NodeI currentTime = currentPrimaryVal.endTime.Add(executor.SyncBound) sk, err := bls.NewSecretKey() if err != nil { - return nil, fmt.Errorf("could not make private key, %w", err) + return nil, fmt.Errorf("could not make private key: %w", err) } endTime := currentTime.Add(defaultMinStakingDuration) @@ -558,7 +565,7 @@ func TestTimestampListGenerator(t *testing.T) { nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { - return fmt.Sprintf("failed building events sequence, %s", err.Error()) + return fmt.Sprintf("failed building events sequence: %s", err.Error()) } if len(validatorsTimes) == 0 { @@ -610,7 +617,7 @@ func TestTimestampListGenerator(t *testing.T) { nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { - return fmt.Sprintf("failed building events sequence, %s", err.Error()) + return fmt.Sprintf("failed building events sequence: %s", err.Error()) } if len(validatorsTimes) == 0 { @@ -662,7 +669,7 @@ func TestTimestampListGenerator(t *testing.T) { nodeID := ids.GenerateTestNodeID() validatorsTimes, err := buildTimestampsList(events, currentTime, nodeID) if err != nil { - return fmt.Sprintf("failed building events sequence, %s", err.Error()) + return fmt.Sprintf("failed building events sequence: %s", err.Error()) } if len(validatorsTimes) == 0 { @@ -817,9 +824,10 @@ func buildCustomGenesis() ([]byte, error) { } } - // we need at least a validator, otherwise BuildBlock would fail, since it won't find - // next staker to promote/evict from stakers set. Contrary to what happens with production code - // we push such validator at the end of times, so to avoid interference with our tests + // we need at least a validator, otherwise BuildBlock would fail, since it + // won't find next staker to promote/evict from stakers set. Contrary to + // what happens with production code we push such validator at the end of + // times, so to avoid interference with our tests nodeID := ids.NodeID(keys[len(keys)-1].PublicKey().Address()) addr, err := address.FormatBech32(constants.UnitTestHRP, nodeID.Bytes()) if err != nil { From acac984848e30e63d05ca446f40c5427e6a24ee2 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 8 Jun 2023 11:53:10 -0400 Subject: [PATCH 26/27] import nit --- vms/platformvm/validators/manager_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 43539771775a..84259df1c45f 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -9,12 +9,13 @@ import ( "time" "github.com/golang/mock/gomock" - "golang.org/x/exp/maps" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "github.com/ava-labs/avalanchego/chains" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" From fa43300a1948bef940aadf250321c875f33a2ea0 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 8 Jun 2023 11:59:09 -0400 Subject: [PATCH 27/27] nit naming --- vms/platformvm/vm_regression_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vms/platformvm/vm_regression_test.go b/vms/platformvm/vm_regression_test.go index 1d77e9429204..16796acacb88 100644 --- a/vms/platformvm/vm_regression_test.go +++ b/vms/platformvm/vm_regression_test.go @@ -1449,9 +1449,9 @@ func TestRemovePermissionedValidatorDuringPendingToCurrentTransitionTracked(t *t require.NoError(vm.SetPreference(context.Background(), vm.manager.LastAccepted())) } -// GetValidatorSet must return the BLS keys for a given validator -// correctly when queried at a previous height, even in case it has currently expired -func Test_RegressionBLSKeyDiff(t *testing.T) { +// GetValidatorSet must return the BLS keys for a given validator correctly when +// queried at a previous height, even in case it has currently expired +func TestSubnetValidatorBLSKeyDiffAfterExpiry(t *testing.T) { // setup require := require.New(t) vm, _, _ := defaultVM() @@ -1729,10 +1729,10 @@ func Test_RegressionBLSKeyDiff(t *testing.T) { } } -func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { - // A primary network validator has an empty BLS key. Then it restakes - // adding the BLS key. Querying the validator set back when BLS key was empty - // must return an empty BLS key. +func TestPrimaryNetworkValidatorPopulatedToEmptyBLSKeyDiff(t *testing.T) { + // A primary network validator has an empty BLS key. Then it restakes adding + // the BLS key. Querying the validator set back when BLS key was empty must + // return an empty BLS key. // setup require := require.New(t) @@ -1888,11 +1888,11 @@ func Test_RegressionPrimaryNetworkValidatorEmptyBLSKeyDiff(t *testing.T) { } } -func Test_RegressionSubnetkValidatorEmptyBLSKeyDiff(t *testing.T) { +func TestSubnetValidatorPopulatedToEmptyBLSKeyDiff(t *testing.T) { // A primary network validator has an empty BLS key and a subnet validator. - // Primary network validator terminates its first staking cycle and it restakes - // adding the BLS key. Querying the validator set back when BLS key was empty - // must return an empty BLS key for the subnet validator + // Primary network validator terminates its first staking cycle and it + // restakes adding the BLS key. Querying the validator set back when BLS key + // was empty must return an empty BLS key for the subnet validator // setup require := require.New(t)