From 795a10a6c0001034e9c5afc4e235cc8b039c756f Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 3 Apr 2023 10:12:55 +0200 Subject: [PATCH 01/16] repack pchain validators --- vms/platformvm/blocks/builder/helpers_test.go | 17 +- vms/platformvm/blocks/executor/acceptor.go | 11 +- .../blocks/executor/acceptor_test.go | 40 +- .../blocks/executor/helpers_test.go | 19 +- vms/platformvm/blocks/executor/manager.go | 12 +- vms/platformvm/service.go | 3 +- vms/platformvm/validators/set.go | 297 ++++++++++++ vms/platformvm/validators/set_test.go | 442 ++++++++++++++++++ vms/platformvm/validators/test_set.go | 37 ++ vms/platformvm/vm.go | 262 +---------- vms/platformvm/vm_test.go | 442 ------------------ 11 files changed, 823 insertions(+), 759 deletions(-) create mode 100644 vms/platformvm/validators/set.go create mode 100644 vms/platformvm/validators/set_test.go create mode 100644 vms/platformvm/validators/test_set.go diff --git a/vms/platformvm/blocks/builder/helpers_test.go b/vms/platformvm/blocks/builder/helpers_test.go index f72cbbe38e3f..22d7444bdf88 100644 --- a/vms/platformvm/blocks/builder/helpers_test.go +++ b/vms/platformvm/blocks/builder/helpers_test.go @@ -34,7 +34,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" @@ -53,13 +52,12 @@ import ( blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" txbuilder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) const ( - testNetworkID = 10 // To be used in tests - defaultWeight = 10000 - maxRecentlyAcceptedWindowSize = 256 - recentlyAcceptedWindowTTL = 5 * time.Minute + testNetworkID = 10 // To be used in tests + defaultWeight = 10000 ) var ( @@ -155,13 +153,6 @@ func newEnvironment(t *testing.T) *environment { } registerer := prometheus.NewRegistry() - window := window.New[ids.ID]( - window.Config{ - Clock: res.clk, - MaxSize: maxRecentlyAcceptedWindowSize, - TTL: recentlyAcceptedWindowTTL, - }, - ) res.sender = &common.SenderTest{T: t} metrics, err := metrics.New("", registerer, res.config.TrackedSubnets) @@ -178,7 +169,7 @@ func newEnvironment(t *testing.T) *environment { metrics, res.state, &res.backend, - window, + &pvalidators.TestSet{}, ) res.Builder = New( diff --git a/vms/platformvm/blocks/executor/acceptor.go b/vms/platformvm/blocks/executor/acceptor.go index 047fd1faa1fd..54b24bbde8e5 100644 --- a/vms/platformvm/blocks/executor/acceptor.go +++ b/vms/platformvm/blocks/executor/acceptor.go @@ -8,13 +8,12 @@ import ( "go.uber.org/zap" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/utils" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) var _ blocks.Visitor = (*acceptor)(nil) @@ -24,9 +23,9 @@ var _ blocks.Visitor = (*acceptor)(nil) // being shutdown. type acceptor struct { *backend - metrics metrics.Metrics - recentlyAccepted window.Window[ids.ID] - bootstrapped *utils.Atomic[bool] + metrics metrics.Metrics + validators validators.Set + bootstrapped *utils.Atomic[bool] } func (a *acceptor) BanffAbortBlock(b *blocks.BanffAbortBlock) error { @@ -305,6 +304,6 @@ func (a *acceptor) commonAccept(b blocks.Block) error { a.state.SetLastAccepted(blkID) a.state.SetHeight(b.Height()) a.state.AddStatelessBlock(b, choices.Accepted) - a.recentlyAccepted.Add(blkID) + a.validators.Track(blkID) return nil } diff --git a/vms/platformvm/blocks/executor/acceptor_test.go b/vms/platformvm/blocks/executor/acceptor_test.go index 583e3ee6d962..c90a9f6aa993 100644 --- a/vms/platformvm/blocks/executor/acceptor_test.go +++ b/vms/platformvm/blocks/executor/acceptor_test.go @@ -5,7 +5,6 @@ package executor import ( "testing" - "time" "github.com/golang/mock/gomock" @@ -19,13 +18,14 @@ import ( "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/timer/mockable" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" + + pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) func TestAcceptorVisitProposalBlock(t *testing.T) { @@ -61,8 +61,8 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { }, state: s, }, - metrics: metrics.Noop, - recentlyAccepted: nil, + metrics: metrics.Noop, + validators: &pvalidators.TestSet{}, } err = acceptor.ApricotProposalBlock(blk) @@ -98,12 +98,8 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { SharedMemory: sharedMemory, }, }, - metrics: metrics.Noop, - recentlyAccepted: window.New[ids.ID](window.Config{ - Clock: &mockable.Clock{}, - MaxSize: 1, - TTL: time.Hour, - }), + metrics: metrics.Noop, + validators: &pvalidators.TestSet{}, } blk, err := blocks.NewApricotAtomicBlock( @@ -183,12 +179,8 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { SharedMemory: sharedMemory, }, }, - metrics: metrics.Noop, - recentlyAccepted: window.New[ids.ID](window.Config{ - Clock: clk, - MaxSize: 1, - TTL: time.Hour, - }), + metrics: metrics.Noop, + validators: &pvalidators.TestSet{}, } blk, err := blocks.NewBanffStandardBlock( @@ -278,12 +270,8 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { SharedMemory: sharedMemory, }, }, - metrics: metrics.Noop, - recentlyAccepted: window.New[ids.ID](window.Config{ - Clock: &mockable.Clock{}, - MaxSize: 1, - TTL: time.Hour, - }), + metrics: metrics.Noop, + validators: &pvalidators.TestSet{}, bootstrapped: &utils.Atomic[bool]{}, } @@ -368,12 +356,8 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { SharedMemory: sharedMemory, }, }, - metrics: metrics.Noop, - recentlyAccepted: window.New[ids.ID](window.Config{ - Clock: &mockable.Clock{}, - MaxSize: 1, - TTL: time.Hour, - }), + metrics: metrics.Noop, + validators: &pvalidators.TestSet{}, bootstrapped: &utils.Atomic[bool]{}, } diff --git a/vms/platformvm/blocks/executor/helpers_test.go b/vms/platformvm/blocks/executor/helpers_test.go index 1a08386777f8..4000579106bb 100644 --- a/vms/platformvm/blocks/executor/helpers_test.go +++ b/vms/platformvm/blocks/executor/helpers_test.go @@ -35,7 +35,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" @@ -54,16 +53,15 @@ import ( db_manager "github.com/ava-labs/avalanchego/database/manager" p_tx_builder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" + pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) const ( pending stakerStatus = iota current - testNetworkID = 10 // To be used in tests - defaultWeight = 10000 - maxRecentlyAcceptedWindowSize = 256 - recentlyAcceptedWindowTTL = 5 * time.Minute + testNetworkID = 10 // To be used in tests + defaultWeight = 10000 ) var ( @@ -190,13 +188,6 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { } registerer := prometheus.NewRegistry() - window := window.New[ids.ID]( - window.Config{ - Clock: res.clk, - MaxSize: maxRecentlyAcceptedWindowSize, - TTL: recentlyAcceptedWindowTTL, - }, - ) res.sender = &common.SenderTest{T: t} metrics := metrics.Noop @@ -213,7 +204,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.state, res.backend, - window, + &pvalidators.TestSet{}, ) addSubnet(res) } else { @@ -222,7 +213,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.mockedState, res.backend, - window, + &pvalidators.TestSet{}, ) // we do not add any subnet to state, since we can mock // whatever we need diff --git a/vms/platformvm/blocks/executor/manager.go b/vms/platformvm/blocks/executor/manager.go index b552bbc12900..17c5b84db70c 100644 --- a/vms/platformvm/blocks/executor/manager.go +++ b/vms/platformvm/blocks/executor/manager.go @@ -6,12 +6,12 @@ package executor import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/consensus/snowman" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/vms/platformvm/blocks" "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" "github.com/ava-labs/avalanchego/vms/platformvm/txs/mempool" + "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) var _ Manager = (*manager)(nil) @@ -31,7 +31,7 @@ func NewManager( metrics metrics.Metrics, s state.State, txExecutorBackend *executor.Backend, - recentlyAccepted window.Window[ids.ID], + validatorsSet validators.Set, ) Manager { backend := &backend{ Mempool: mempool, @@ -48,10 +48,10 @@ func NewManager( txExecutorBackend: txExecutorBackend, }, acceptor: &acceptor{ - backend: backend, - metrics: metrics, - recentlyAccepted: recentlyAccepted, - bootstrapped: txExecutorBackend.Bootstrapped, + backend: backend, + metrics: metrics, + validators: validatorsSet, + bootstrapped: txExecutorBackend.Bootstrapped, }, rejector: &rejector{backend: backend}, } diff --git a/vms/platformvm/service.go b/vms/platformvm/service.go index 422860a5caa3..d1e877af1305 100644 --- a/vms/platformvm/service.go +++ b/vms/platformvm/service.go @@ -40,6 +40,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" + "github.com/ava-labs/avalanchego/vms/platformvm/validators" "github.com/ava-labs/avalanchego/vms/secp256k1fx" platformapi "github.com/ava-labs/avalanchego/vms/platformvm/api" @@ -2396,7 +2397,7 @@ func (s *Service) GetTotalStake(_ *http.Request, args *GetTotalStakeArgs, reply vdrs, ok := s.vm.Validators.Get(args.SubnetID) if !ok { - return errMissingValidatorSet + return validators.ErrMissingValidatorSet } weight := json.Uint64(vdrs.Weight()) reply.Weight = weight diff --git a/vms/platformvm/validators/set.go b/vms/platformvm/validators/set.go new file mode 100644 index 000000000000..0d6feb63d84c --- /dev/null +++ b/vms/platformvm/validators/set.go @@ -0,0 +1,297 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package validators + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/math" + "github.com/ava-labs/avalanchego/utils/timer/mockable" + "github.com/ava-labs/avalanchego/utils/window" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/metrics" + "github.com/ava-labs/avalanchego/vms/platformvm/state" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +const ( + validatorSetsCacheSize = 64 + maxRecentlyAcceptedWindowSize = 256 + recentlyAcceptedWindowTTL = 5 * time.Minute +) + +var ( + _ validators.State = (*set)(nil) + + ErrMissingValidatorSet = errors.New("missing validator set") +) + +// P-chain must be able to provide information about validators active +// at different heights. [Set] interface encapsulates all the machinery +// to achieve this. +type Set interface { + validators.State + + GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) + Track(blkID ids.ID) +} + +func NewSet( + cfg config.Config, + state state.State, + metrics metrics.Metrics, + clk mockable.Clock, +) Set { + return &set{ + cfg: cfg, + state: state, + metrics: metrics, + clk: &clk, + caches: make(map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]), + recentlyAccepted: window.New[ids.ID]( + window.Config{ + Clock: &clk, + MaxSize: maxRecentlyAcceptedWindowSize, + TTL: recentlyAcceptedWindowTTL, + }, + ), + } +} + +type set struct { + cfg config.Config + state state.State + metrics metrics.Metrics + clk *mockable.Clock + + // Maps caches for each subnet that is currently tracked. + // Key: Subnet ID + // Value: cache mapping height -> validator set map + caches map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput] + + // sliding window of blocks that were recently accepted + recentlyAccepted window.Window[ids.ID] +} + +// GetMinimumHeight returns the height of the most recent block beyond the +// horizon of our recentlyAccepted window. +// +// Because the time between blocks is arbitrary, we're only guaranteed that +// the window's configured TTL amount of time has passed once an element +// expires from the window. +// +// To try to always return a block older than the window's TTL, we return the +// parent of the oldest element in the window (as an expired element is always +// guaranteed to be sufficiently stale). If we haven't expired an element yet +// in the case of a process restart, we default to the lastAccepted block's +// height which is likely (but not guaranteed) to also be older than the +// window's configured TTL. +// +// If [UseCurrentHeight] is true, we will always return the last accepted block +// height as the minimum. This is used to trigger the proposervm on recently +// created subnets before [recentlyAcceptedWindowTTL]. +func (vs *set) GetMinimumHeight(ctx context.Context) (uint64, error) { + if vs.cfg.UseCurrentHeight { + return vs.GetCurrentHeight(ctx) + } + + oldest, ok := vs.recentlyAccepted.Oldest() + if !ok { + return vs.GetCurrentHeight(ctx) + } + + blk, _, err := vs.state.GetStatelessBlock(oldest) + if err != nil { + return 0, err + } + + // We subtract 1 from the height of [oldest] because we want the height of + // the last block accepted before the [recentlyAccepted] window. + // + // There is guaranteed to be a block accepted before this window because the + // first block added to [recentlyAccepted] window is >= height 1. + return blk.Height() - 1, nil +} + +// GetCurrentHeight returns the height of the last accepted block +func (vs *set) GetCurrentHeight(context.Context) (uint64, error) { + lastAcceptedID := vs.state.GetLastAccepted() + lastAccepted, _, err := vs.state.GetStatelessBlock(lastAcceptedID) + if err != nil { + return 0, err + } + return lastAccepted.Height(), nil +} + +// GetValidatorSet returns the validator set at the specified height for the +// provided subnetID. +func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + validatorSetsCache, exists := vs.caches[subnetID] + if !exists { + validatorSetsCache = &cache.LRU[uint64, map[ids.NodeID]*validators.GetValidatorOutput]{Size: validatorSetsCacheSize} + // Only cache tracked subnets + if subnetID == constants.PrimaryNetworkID || vs.cfg.TrackedSubnets.Contains(subnetID) { + vs.caches[subnetID] = validatorSetsCache + } + } + + if validatorSet, ok := validatorSetsCache.Get(height); ok { + vs.metrics.IncValidatorSetsCached() + return validatorSet, nil + } + + lastAcceptedHeight, err := vs.GetCurrentHeight(ctx) + if err != nil { + return nil, err + } + if lastAcceptedHeight < height { + return nil, database.ErrNotFound + } + + // get the start time to track metrics + startTime := vs.clk.Time() + + currentSubnetValidators, ok := vs.cfg.Validators.Get(subnetID) + if !ok { + currentSubnetValidators = validators.NewSet() + if err := vs.state.ValidatorSet(subnetID, currentSubnetValidators); err != nil { + return nil, err + } + } + currentPrimaryNetworkValidators, ok := vs.cfg.Validators.Get(constants.PrimaryNetworkID) + if !ok { + // This should never happen + return nil, ErrMissingValidatorSet + } + + 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", ErrMissingValidatorSet, vdr.NodeID) + } + vdrSet[vdr.NodeID] = &validators.GetValidatorOutput{ + NodeID: vdr.NodeID, + PublicKey: primaryVdr.PublicKey, + Weight: vdr.Weight, + } + } + + for i := lastAcceptedHeight; i > height; i-- { + weightDiffs, err := vs.state.GetValidatorWeightDiffs(i, subnetID) + if err != nil { + return nil, 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. + var op func(uint64, uint64) (uint64, error) + if weightDiff.Decrease { + // The validator's weight was decreased at this block, so in the + // prior block it was higher. + op = math.Add64 + } else { + // The validator's weight was increased at this block, so in the + // prior block it was lower. + op = math.Sub[uint64] + } + + // Apply the weight change. + vdr.Weight, err = op(vdr.Weight, weightDiff.Amount) + if err != nil { + return nil, 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 := vs.state.GetValidatorPublicKeyDiffs(i) + if err != nil { + return nil, 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 { + // 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, vdrSet) + + endTime := vs.clk.Time() + vs.metrics.IncValidatorSetsCreated() + vs.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) + vs.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) + return vdrSet, nil +} + +// GetCurrentHeight returns the height of the last accepted block +func (vs *set) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { + if chainID == constants.PlatformChainID { + return constants.PrimaryNetworkID, nil + } + + chainTx, _, err := vs.state.GetTx(chainID) + if err != nil { + return ids.Empty, fmt.Errorf( + "problem retrieving blockchain %q: %w", + chainID, + err, + ) + } + chain, ok := chainTx.Unsigned.(*txs.CreateChainTx) + if !ok { + return ids.Empty, fmt.Errorf("%q is not a blockchain", chainID) + } + return chain.SubnetID, nil +} + +func (vs *set) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { + validatorSet, exist := vs.cfg.Validators.Get(subnetID) + if !exist { + return nil, false + } + validators := validatorSet.List() + + validatorIDs := make([]ids.NodeID, len(validators)) + for i, vdr := range validators { + validatorIDs[i] = vdr.NodeID + } + + return validatorIDs, true +} + +func (vs *set) Track(blkID ids.ID) { + vs.recentlyAccepted.Add(blkID) +} diff --git a/vms/platformvm/validators/set_test.go b/vms/platformvm/validators/set_test.go new file mode 100644 index 000000000000..77b0d9f29a9c --- /dev/null +++ b/vms/platformvm/validators/set_test.go @@ -0,0 +1,442 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package validators + +import ( + "context" + "testing" + "time" + + "github.com/ava-labs/avalanchego/chains" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/choices" + "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/timer/mockable" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/platformvm/blocks" + "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/state" + "github.com/golang/mock/gomock" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" +) + +// AVAX asset ID in tests +var defaultRewardConfig = reward.Config{ + MaxConsumptionRate: .12 * reward.PercentDenominator, + MinConsumptionRate: .10 * reward.PercentDenominator, + MintingPeriod: 365 * 24 * time.Hour, + SupplyCap: 720 * units.MegaAvax, +} + +func TestVM_GetValidatorSet(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Populate the validator set to use below + var ( + numVdrs = 4 + vdrBaseWeight = uint64(1_000) + vdrs []*validators.Validator + ) + + for i := 0; i < numVdrs; i++ { + sk, err := bls.NewSecretKey() + require.NoError(t, err) + + vdrs = append(vdrs, &validators.Validator{ + NodeID: ids.GenerateTestNodeID(), + PublicKey: bls.PublicFromSecretKey(sk), + Weight: vdrBaseWeight + uint64(i), + }) + } + + type test struct { + name string + // Height we're getting the diff at + height uint64 + lastAcceptedHeight uint64 + subnetID ids.ID + // Validator sets at tip + currentPrimaryNetworkValidators []*validators.Validator + currentSubnetValidators []*validators.Validator + // Diff at tip, block before tip, etc. + // This must have [height] - [lastAcceptedHeight] elements + weightDiffs []map[ids.NodeID]*state.ValidatorWeightDiff + // Diff at tip, block before tip, etc. + // This must have [height] - [lastAcceptedHeight] elements + pkDiffs []map[ids.NodeID]*bls.PublicKey + expectedVdrSet map[ids.NodeID]*validators.GetValidatorOutput + expectedErr error + } + + tests := []test{ + { + name: "after tip", + height: 1, + lastAcceptedHeight: 0, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{}, + expectedErr: database.ErrNotFound, + }, + { + name: "at tip", + height: 1, + lastAcceptedHeight: 1, + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + }, + currentSubnetValidators: []*validators.Validator{ + copySubnetValidator(vdrs[0]), + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight, + }, + }, + expectedErr: nil, + }, + { + name: "1 before tip", + height: 2, + lastAcceptedHeight: 3, + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + copyPrimaryValidator(vdrs[1]), + }, + currentSubnetValidators: []*validators.Validator{ + // At tip we have these 2 validators + copySubnetValidator(vdrs[0]), + copySubnetValidator(vdrs[1]), + }, + weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ + { + // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, + // and vdrs[2] left + vdrs[0].NodeID: { + Decrease: true, + Amount: 1, + }, + vdrs[1].NodeID: { + Decrease: false, + Amount: 1, + }, + vdrs[2].NodeID: { + Decrease: true, + Amount: vdrs[2].Weight, + }, + }, + }, + pkDiffs: []map[ids.NodeID]*bls.PublicKey{ + { + vdrs[2].NodeID: vdrs[2].PublicKey, + }, + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight + 1, + }, + vdrs[1].NodeID: { + NodeID: vdrs[1].NodeID, + PublicKey: vdrs[1].PublicKey, + Weight: vdrs[1].Weight - 1, + }, + vdrs[2].NodeID: { + NodeID: vdrs[2].NodeID, + PublicKey: vdrs[2].PublicKey, + Weight: vdrs[2].Weight, + }, + }, + expectedErr: nil, + }, + { + name: "2 before tip", + height: 3, + lastAcceptedHeight: 5, + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + copyPrimaryValidator(vdrs[1]), + }, + currentSubnetValidators: []*validators.Validator{ + // At tip we have these 2 validators + copySubnetValidator(vdrs[0]), + copySubnetValidator(vdrs[1]), + }, + weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ + { + // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, + // and vdrs[2] left + vdrs[0].NodeID: { + Decrease: true, + Amount: 1, + }, + vdrs[1].NodeID: { + Decrease: false, + Amount: 1, + }, + vdrs[2].NodeID: { + Decrease: true, + Amount: vdrs[2].Weight, + }, + }, + { + // At the block before tip vdrs[0] lost weight, vdrs[1] gained weight, + // vdrs[2] joined + vdrs[0].NodeID: { + Decrease: true, + Amount: 1, + }, + vdrs[1].NodeID: { + Decrease: false, + Amount: 1, + }, + vdrs[2].NodeID: { + Decrease: false, + Amount: vdrs[2].Weight, + }, + }, + }, + pkDiffs: []map[ids.NodeID]*bls.PublicKey{ + { + vdrs[2].NodeID: vdrs[2].PublicKey, + }, + {}, + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight + 2, + }, + vdrs[1].NodeID: { + NodeID: vdrs[1].NodeID, + PublicKey: vdrs[1].PublicKey, + Weight: vdrs[1].Weight - 2, + }, + }, + expectedErr: nil, + }, + { + name: "1 before tip; nil public key", + height: 4, + lastAcceptedHeight: 5, + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + copyPrimaryValidator(vdrs[1]), + }, + currentSubnetValidators: []*validators.Validator{ + // At tip we have these 2 validators + copySubnetValidator(vdrs[0]), + copySubnetValidator(vdrs[1]), + }, + weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ + { + // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, + // and vdrs[2] left + vdrs[0].NodeID: { + Decrease: true, + Amount: 1, + }, + vdrs[1].NodeID: { + Decrease: false, + Amount: 1, + }, + vdrs[2].NodeID: { + Decrease: true, + Amount: vdrs[2].Weight, + }, + }, + }, + pkDiffs: []map[ids.NodeID]*bls.PublicKey{ + {}, + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight + 1, + }, + vdrs[1].NodeID: { + NodeID: vdrs[1].NodeID, + PublicKey: vdrs[1].PublicKey, + Weight: vdrs[1].Weight - 1, + }, + vdrs[2].NodeID: { + NodeID: vdrs[2].NodeID, + Weight: vdrs[2].Weight, + }, + }, + expectedErr: nil, + }, + { + name: "1 before tip; subnet", + height: 5, + lastAcceptedHeight: 6, + subnetID: ids.GenerateTestID(), + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + copyPrimaryValidator(vdrs[1]), + copyPrimaryValidator(vdrs[3]), + }, + currentSubnetValidators: []*validators.Validator{ + // At tip we have these 2 validators + copySubnetValidator(vdrs[0]), + copySubnetValidator(vdrs[1]), + }, + weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ + { + // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, + // and vdrs[2] left + vdrs[0].NodeID: { + Decrease: true, + Amount: 1, + }, + vdrs[1].NodeID: { + Decrease: false, + Amount: 1, + }, + vdrs[2].NodeID: { + Decrease: true, + Amount: vdrs[2].Weight, + }, + }, + }, + pkDiffs: []map[ids.NodeID]*bls.PublicKey{ + {}, + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight + 1, + }, + vdrs[1].NodeID: { + NodeID: vdrs[1].NodeID, + PublicKey: vdrs[1].PublicKey, + Weight: vdrs[1].Weight - 1, + }, + vdrs[2].NodeID: { + NodeID: vdrs[2].NodeID, + Weight: vdrs[2].Weight, + }, + }, + expectedErr: nil, + }, + { + name: "unrelated primary network key removal on subnet lookup", + height: 4, + lastAcceptedHeight: 5, + subnetID: ids.GenerateTestID(), + currentPrimaryNetworkValidators: []*validators.Validator{ + copyPrimaryValidator(vdrs[0]), + }, + currentSubnetValidators: []*validators.Validator{ + copySubnetValidator(vdrs[0]), + }, + weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ + {}, + }, + pkDiffs: []map[ids.NodeID]*bls.PublicKey{ + { + vdrs[1].NodeID: vdrs[1].PublicKey, + }, + }, + expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ + vdrs[0].NodeID: { + NodeID: vdrs[0].NodeID, + PublicKey: vdrs[0].PublicKey, + Weight: vdrs[0].Weight, + }, + }, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := require.New(t) + + // setup validators set + vdrs := validators.NewMockManager(ctrl) + cfg := config.Config{ + Chains: chains.TestManager, + UptimePercentage: .2, + RewardConfig: defaultRewardConfig, + Validators: vdrs, + UptimeLockedCalculator: uptime.NewLockedCalculator(), + BanffTime: mockable.MaxTime, + } + mockState := state.NewMockState(ctrl) + + metrics, err := metrics.New("", prometheus.NewRegistry(), cfg.TrackedSubnets) + r.NoError(err) + + clk := mockable.Clock{} + validatorssSet := NewSet(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 := mockSubnetVdrSet + if tt.subnetID != constants.PrimaryNetworkID { + mockPrimaryVdrSet = validators.NewMockSet(ctrl) + vdrs.EXPECT().Get(constants.PrimaryNetworkID).Return(mockPrimaryVdrSet, true).AnyTimes() + } + for _, vdr := range tt.currentPrimaryNetworkValidators { + mockPrimaryVdrSet.EXPECT().Get(vdr.NodeID).Return(vdr, true).AnyTimes() + } + + // Tell state what diffs to report + for _, weightDiff := range tt.weightDiffs { + mockState.EXPECT().GetValidatorWeightDiffs(gomock.Any(), gomock.Any()).Return(weightDiff, nil) + } + + for _, pkDiff := range tt.pkDiffs { + mockState.EXPECT().GetValidatorPublicKeyDiffs(gomock.Any()).Return(pkDiff, nil) + } + + // Tell state last accepted block to report + mockTip := blocks.NewMockBlock(ctrl) + mockTip.EXPECT().Height().Return(tt.lastAcceptedHeight) + mockTipID := ids.GenerateTestID() + mockState.EXPECT().GetLastAccepted().Return(mockTipID) + mockState.EXPECT().GetStatelessBlock(mockTipID).Return(mockTip, choices.Accepted, nil) + + // Compute validator set at previous height + gotVdrSet, err := validatorssSet.GetValidatorSet(context.Background(), tt.height, tt.subnetID) + r.ErrorIs(err, tt.expectedErr) + if tt.expectedErr != nil { + return + } + r.Equal(len(tt.expectedVdrSet), len(gotVdrSet)) + for nodeID, vdr := range tt.expectedVdrSet { + otherVdr, ok := gotVdrSet[nodeID] + r.True(ok) + r.Equal(vdr, otherVdr) + } + }) + } +} + +func copyPrimaryValidator(vdr *validators.Validator) *validators.Validator { + newVdr := *vdr + return &newVdr +} + +func copySubnetValidator(vdr *validators.Validator) *validators.Validator { + newVdr := *vdr + newVdr.PublicKey = nil + return &newVdr +} diff --git a/vms/platformvm/validators/test_set.go b/vms/platformvm/validators/test_set.go new file mode 100644 index 000000000000..7ec649ced696 --- /dev/null +++ b/vms/platformvm/validators/test_set.go @@ -0,0 +1,37 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package validators + +import ( + "context" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" +) + +var _ validators.State = (*TestSet)(nil) + +type TestSet struct{} + +func (*TestSet) GetMinimumHeight(_ context.Context) (uint64, error) { + return 0, nil +} + +func (*TestSet) GetCurrentHeight(_ context.Context) (uint64, error) { + return 0, nil +} + +func (*TestSet) GetSubnetID(_ context.Context, _ ids.ID) (ids.ID, error) { + return ids.Empty, nil +} + +func (*TestSet) GetValidatorSet(_ context.Context, _ uint64, _ ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return nil, nil +} + +func (*TestSet) GetValidatorIDs(_ ids.ID) ([]ids.NodeID, bool) { + return nil, false +} + +func (*TestSet) Track(_ ids.ID) {} diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index faaf7f5c376d..cfc2f26e0ae4 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -5,9 +5,7 @@ package platformvm import ( "context" - "errors" "fmt" - "time" "github.com/gorilla/rpc/v2" @@ -18,7 +16,6 @@ import ( "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" - "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/manager" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow" @@ -33,7 +30,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/timer/mockable" - "github.com/ava-labs/avalanchego/utils/window" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/components/avax" @@ -53,12 +49,7 @@ import ( blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/blocks/executor" txbuilder "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" -) - -const ( - validatorSetsCacheSize = 512 - maxRecentlyAcceptedWindowSize = 256 - recentlyAcceptedWindowTTL = 5 * time.Minute + pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) var ( @@ -66,14 +57,12 @@ var ( _ secp256k1fx.VM = (*VM)(nil) _ validators.State = (*VM)(nil) _ validators.SubnetConnector = (*VM)(nil) - - errMissingValidatorSet = errors.New("missing validator set") - errMissingValidator = errors.New("missing validator") ) type VM struct { config.Config blockbuilder.Builder + pvalidators.Set metrics metrics.Metrics atomicUtxosManager avax.AtomicUTXOManager @@ -95,14 +84,6 @@ type VM struct { // Bootstrapped remembers if this chain has finished bootstrapping or not bootstrapped utils.Atomic[bool] - // Maps caches for each subnet that is currently tracked. - // Key: Subnet ID - // Value: cache mapping height -> validator set map - validatorSetCaches map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput] - - // sliding window of blocks that were recently accepted - recentlyAccepted window.Window[ids.ID] - txBuilder txbuilder.Builder manager blockexecutor.Manager } @@ -143,15 +124,6 @@ func (vm *VM) Initialize( return err } - vm.validatorSetCaches = make(map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]) - vm.recentlyAccepted = window.New[ids.ID]( - window.Config{ - Clock: &vm.clock, - MaxSize: maxRecentlyAcceptedWindowSize, - TTL: recentlyAcceptedWindowTTL, - }, - ) - rewards := reward.NewCalculator(vm.RewardConfig) vm.state, err = state.New( vm.dbManager.Current().Database, @@ -167,6 +139,7 @@ func (vm *VM) Initialize( return err } + vm.Set = pvalidators.NewSet(vm.Config, vm.state, vm.metrics, vm.clock) vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) @@ -205,7 +178,7 @@ func (vm *VM) Initialize( vm.metrics, vm.state, txExecutorBackend, - vm.recentlyAccepted, + vm.Set, ) vm.Builder = blockbuilder.New( mempool, @@ -290,18 +263,18 @@ func (vm *VM) onNormalOperationsStarted() error { return err } - primaryVdrIDs, exists := vm.getValidatorIDs(constants.PrimaryNetworkID) + primaryVdrIDs, exists := vm.GetValidatorIDs(constants.PrimaryNetworkID) if !exists { - return errMissingValidatorSet + return pvalidators.ErrMissingValidatorSet } if err := vm.uptimeManager.StartTracking(primaryVdrIDs, constants.PrimaryNetworkID); err != nil { return err } for subnetID := range vm.TrackedSubnets { - vdrIDs, exists := vm.getValidatorIDs(subnetID) + vdrIDs, exists := vm.GetValidatorIDs(subnetID) if !exists { - return errMissingValidatorSet + return pvalidators.ErrMissingValidatorSet } if err := vm.uptimeManager.StartTracking(vdrIDs, subnetID); err != nil { return err @@ -337,18 +310,18 @@ func (vm *VM) Shutdown(context.Context) error { vm.Builder.Shutdown() if vm.bootstrapped.Get() { - primaryVdrIDs, exists := vm.getValidatorIDs(constants.PrimaryNetworkID) + primaryVdrIDs, exists := vm.GetValidatorIDs(constants.PrimaryNetworkID) if !exists { - return errMissingValidatorSet + return pvalidators.ErrMissingValidatorSet } if err := vm.uptimeManager.StopTracking(primaryVdrIDs, constants.PrimaryNetworkID); err != nil { return err } for subnetID := range vm.TrackedSubnets { - vdrIDs, exists := vm.getValidatorIDs(subnetID) + vdrIDs, exists := vm.GetValidatorIDs(subnetID) if !exists { - return errMissingValidatorSet + return pvalidators.ErrMissingValidatorSet } if err := vm.uptimeManager.StopTracking(vdrIDs, subnetID); err != nil { return err @@ -368,21 +341,6 @@ func (vm *VM) Shutdown(context.Context) error { return errs.Err } -func (vm *VM) getValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { - validatorSet, exist := vm.Validators.Get(subnetID) - if !exist { - return nil, false - } - validators := validatorSet.List() - - validatorIDs := make([]ids.NodeID, len(validators)) - for i, vdr := range validators { - validatorIDs[i] = vdr.NodeID - } - - return validatorIDs, true -} - func (vm *VM) ParseBlock(_ context.Context, b []byte) (snowman.Block, error) { // Note: blocks to be parsed are not verified, so we must used blocks.Codec // rather than blocks.GenesisCodec @@ -475,200 +433,6 @@ func (vm *VM) Disconnected(_ context.Context, nodeID ids.NodeID) error { return vm.state.Commit() } -// GetValidatorSet returns the validator set at the specified height for the -// provided subnetID. -func (vm *VM) GetValidatorSet(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - validatorSetsCache, exists := vm.validatorSetCaches[subnetID] - if !exists { - validatorSetsCache = &cache.LRU[uint64, map[ids.NodeID]*validators.GetValidatorOutput]{Size: validatorSetsCacheSize} - // Only cache tracked subnets - if subnetID == constants.PrimaryNetworkID || vm.TrackedSubnets.Contains(subnetID) { - vm.validatorSetCaches[subnetID] = validatorSetsCache - } - } - - if validatorSet, ok := validatorSetsCache.Get(height); ok { - vm.metrics.IncValidatorSetsCached() - return validatorSet, nil - } - - lastAcceptedHeight, err := vm.GetCurrentHeight(ctx) - if err != nil { - return nil, err - } - if lastAcceptedHeight < height { - return nil, database.ErrNotFound - } - - // get the start time to track metrics - startTime := vm.Clock().Time() - - currentSubnetValidators, ok := vm.Validators.Get(subnetID) - if !ok { - currentSubnetValidators = validators.NewSet() - if err := vm.state.ValidatorSet(subnetID, currentSubnetValidators); err != nil { - return nil, err - } - } - currentPrimaryNetworkValidators, ok := vm.Validators.Get(constants.PrimaryNetworkID) - if !ok { - // This should never happen - return nil, errMissingValidatorSet - } - - 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{ - NodeID: vdr.NodeID, - PublicKey: primaryVdr.PublicKey, - Weight: vdr.Weight, - } - } - - for i := lastAcceptedHeight; i > height; i-- { - weightDiffs, err := vm.state.GetValidatorWeightDiffs(i, subnetID) - if err != nil { - return nil, 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. - var op func(uint64, uint64) (uint64, error) - if weightDiff.Decrease { - // The validator's weight was decreased at this block, so in the - // prior block it was higher. - op = math.Add64 - } else { - // The validator's weight was increased at this block, so in the - // prior block it was lower. - op = math.Sub[uint64] - } - - // Apply the weight change. - vdr.Weight, err = op(vdr.Weight, weightDiff.Amount) - if err != nil { - return nil, 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 := vm.state.GetValidatorPublicKeyDiffs(i) - if err != nil { - return nil, 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 { - // 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, vdrSet) - - endTime := vm.Clock().Time() - vm.metrics.IncValidatorSetsCreated() - vm.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) - vm.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) - return vdrSet, nil -} - -// GetCurrentHeight returns the height of the last accepted block -func (vm *VM) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { - if chainID == constants.PlatformChainID { - return constants.PrimaryNetworkID, nil - } - - chainTx, _, err := vm.state.GetTx(chainID) - if err != nil { - return ids.Empty, fmt.Errorf( - "problem retrieving blockchain %q: %w", - chainID, - err, - ) - } - chain, ok := chainTx.Unsigned.(*txs.CreateChainTx) - if !ok { - return ids.Empty, fmt.Errorf("%q is not a blockchain", chainID) - } - return chain.SubnetID, nil -} - -// GetMinimumHeight returns the height of the most recent block beyond the -// horizon of our recentlyAccepted window. -// -// Because the time between blocks is arbitrary, we're only guaranteed that -// the window's configured TTL amount of time has passed once an element -// expires from the window. -// -// To try to always return a block older than the window's TTL, we return the -// parent of the oldest element in the window (as an expired element is always -// guaranteed to be sufficiently stale). If we haven't expired an element yet -// in the case of a process restart, we default to the lastAccepted block's -// height which is likely (but not guaranteed) to also be older than the -// window's configured TTL. -// -// If [UseCurrentHeight] is true, we will always return the last accepted block -// height as the minimum. This is used to trigger the proposervm on recently -// created subnets before [recentlyAcceptedWindowTTL]. -func (vm *VM) GetMinimumHeight(ctx context.Context) (uint64, error) { - if vm.Config.UseCurrentHeight { - return vm.GetCurrentHeight(ctx) - } - - oldest, ok := vm.recentlyAccepted.Oldest() - if !ok { - return vm.GetCurrentHeight(ctx) - } - - blk, err := vm.manager.GetBlock(oldest) - if err != nil { - return 0, err - } - - // We subtract 1 from the height of [oldest] because we want the height of - // the last block accepted before the [recentlyAccepted] window. - // - // There is guaranteed to be a block accepted before this window because the - // first block added to [recentlyAccepted] window is >= height 1. - return blk.Height() - 1, nil -} - -// GetCurrentHeight returns the height of the last accepted block -func (vm *VM) GetCurrentHeight(context.Context) (uint64, error) { - lastAccepted, err := vm.manager.GetBlock(vm.state.GetLastAccepted()) - if err != nil { - return 0, err - } - return lastAccepted.Height(), nil -} - func (vm *VM) CodecRegistry() codec.Registry { return vm.codecRegistry } @@ -686,7 +450,7 @@ func (vm *VM) Logger() logging.Logger { func (vm *VM) getPercentConnected(subnetID ids.ID) (float64, error) { vdrSet, exists := vm.Validators.Get(subnetID) if !exists { - return 0, errMissingValidatorSet + return 0, pvalidators.ErrMissingValidatorSet } vdrSetWeight := vdrSet.Weight() diff --git a/vms/platformvm/vm_test.go b/vms/platformvm/vm_test.go index 311ee823f33c..e990a8beddd4 100644 --- a/vms/platformvm/vm_test.go +++ b/vms/platformvm/vm_test.go @@ -11,8 +11,6 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" @@ -41,7 +39,6 @@ import ( "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/subnets" "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" @@ -51,7 +48,6 @@ import ( "github.com/ava-labs/avalanchego/utils/resource" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/avalanchego/utils/timer/mockable" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/version" @@ -60,7 +56,6 @@ import ( "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/state" "github.com/ava-labs/avalanchego/vms/platformvm/status" "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" @@ -2385,443 +2380,6 @@ func TestUptimeDisallowedAfterNeverConnecting(t *testing.T) { require.ErrorIs(err, database.ErrNotFound) } -func TestVM_GetValidatorSet(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // Setup VM - _, genesisBytes := defaultGenesis() - db := manager.NewMemDB(version.Semantic1_0_0) - - vdrManager := validators.NewManager() - primaryVdrs := validators.NewSet() - _ = vdrManager.Add(constants.PrimaryNetworkID, primaryVdrs) - - vm := &VM{Config: config.Config{ - Chains: chains.TestManager, - UptimePercentage: .2, - RewardConfig: defaultRewardConfig, - Validators: vdrManager, - UptimeLockedCalculator: uptime.NewLockedCalculator(), - BanffTime: mockable.MaxTime, - }} - - ctx := defaultContext() - ctx.Lock.Lock() - - msgChan := make(chan common.Message, 1) - appSender := &common.SenderTest{T: t} - err := vm.Initialize(context.Background(), ctx, db, genesisBytes, nil, nil, msgChan, nil, appSender) - require.NoError(t, err) - defer func() { - require.NoError(t, vm.Shutdown(context.Background())) - ctx.Lock.Unlock() - }() - - vm.clock.Set(defaultGenesisTime) - vm.uptimeManager.(uptime.TestManager).SetTime(defaultGenesisTime) - - require.NoError(t, vm.SetState(context.Background(), snow.Bootstrapping)) - require.NoError(t, vm.SetState(context.Background(), snow.NormalOp)) - - var ( - oldVdrs = vm.Validators - oldState = vm.state - numVdrs = 4 - vdrBaseWeight = uint64(1_000) - vdrs []*validators.Validator - ) - // Populate the validator set to use below - for i := 0; i < numVdrs; i++ { - sk, err := bls.NewSecretKey() - require.NoError(t, err) - - vdrs = append(vdrs, &validators.Validator{ - NodeID: ids.GenerateTestNodeID(), - PublicKey: bls.PublicFromSecretKey(sk), - Weight: vdrBaseWeight + uint64(i), - }) - } - - type test struct { - name string - // Height we're getting the diff at - height uint64 - lastAcceptedHeight uint64 - subnetID ids.ID - // Validator sets at tip - currentPrimaryNetworkValidators []*validators.Validator - currentSubnetValidators []*validators.Validator - // Diff at tip, block before tip, etc. - // This must have [height] - [lastAcceptedHeight] elements - weightDiffs []map[ids.NodeID]*state.ValidatorWeightDiff - // Diff at tip, block before tip, etc. - // This must have [height] - [lastAcceptedHeight] elements - pkDiffs []map[ids.NodeID]*bls.PublicKey - expectedVdrSet map[ids.NodeID]*validators.GetValidatorOutput - expectedErr error - } - - tests := []test{ - { - name: "after tip", - height: 1, - lastAcceptedHeight: 0, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{}, - expectedErr: database.ErrNotFound, - }, - { - name: "at tip", - height: 1, - lastAcceptedHeight: 1, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - }, - currentSubnetValidators: []*validators.Validator{ - copySubnetValidator(vdrs[0]), - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight, - }, - }, - expectedErr: nil, - }, - { - name: "1 before tip", - height: 2, - lastAcceptedHeight: 3, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), - }, - currentSubnetValidators: []*validators.Validator{ - // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), - }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { - // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, - // and vdrs[2] left - vdrs[0].NodeID: { - Decrease: true, - Amount: 1, - }, - vdrs[1].NodeID: { - Decrease: false, - Amount: 1, - }, - vdrs[2].NodeID: { - Decrease: true, - Amount: vdrs[2].Weight, - }, - }, - }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[2].NodeID: vdrs[2].PublicKey, - }, - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, - }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, - }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - PublicKey: vdrs[2].PublicKey, - Weight: vdrs[2].Weight, - }, - }, - expectedErr: nil, - }, - { - name: "2 before tip", - height: 3, - lastAcceptedHeight: 5, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), - }, - currentSubnetValidators: []*validators.Validator{ - // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), - }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { - // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, - // and vdrs[2] left - vdrs[0].NodeID: { - Decrease: true, - Amount: 1, - }, - vdrs[1].NodeID: { - Decrease: false, - Amount: 1, - }, - vdrs[2].NodeID: { - Decrease: true, - Amount: vdrs[2].Weight, - }, - }, - { - // At the block before tip vdrs[0] lost weight, vdrs[1] gained weight, - // vdrs[2] joined - vdrs[0].NodeID: { - Decrease: true, - Amount: 1, - }, - vdrs[1].NodeID: { - Decrease: false, - Amount: 1, - }, - vdrs[2].NodeID: { - Decrease: false, - Amount: vdrs[2].Weight, - }, - }, - }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[2].NodeID: vdrs[2].PublicKey, - }, - {}, - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 2, - }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 2, - }, - }, - expectedErr: nil, - }, - { - name: "1 before tip; nil public key", - height: 4, - lastAcceptedHeight: 5, - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), - }, - currentSubnetValidators: []*validators.Validator{ - // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), - }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { - // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, - // and vdrs[2] left - vdrs[0].NodeID: { - Decrease: true, - Amount: 1, - }, - vdrs[1].NodeID: { - Decrease: false, - Amount: 1, - }, - vdrs[2].NodeID: { - Decrease: true, - Amount: vdrs[2].Weight, - }, - }, - }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - {}, - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, - }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, - }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - Weight: vdrs[2].Weight, - }, - }, - expectedErr: nil, - }, - { - name: "1 before tip; subnet", - height: 5, - lastAcceptedHeight: 6, - subnetID: ids.GenerateTestID(), - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - copyPrimaryValidator(vdrs[1]), - copyPrimaryValidator(vdrs[3]), - }, - currentSubnetValidators: []*validators.Validator{ - // At tip we have these 2 validators - copySubnetValidator(vdrs[0]), - copySubnetValidator(vdrs[1]), - }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - { - // At the tip block vdrs[0] lost weight, vdrs[1] gained weight, - // and vdrs[2] left - vdrs[0].NodeID: { - Decrease: true, - Amount: 1, - }, - vdrs[1].NodeID: { - Decrease: false, - Amount: 1, - }, - vdrs[2].NodeID: { - Decrease: true, - Amount: vdrs[2].Weight, - }, - }, - }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - {}, - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight + 1, - }, - vdrs[1].NodeID: { - NodeID: vdrs[1].NodeID, - PublicKey: vdrs[1].PublicKey, - Weight: vdrs[1].Weight - 1, - }, - vdrs[2].NodeID: { - NodeID: vdrs[2].NodeID, - Weight: vdrs[2].Weight, - }, - }, - expectedErr: nil, - }, - { - name: "unrelated primary network key removal on subnet lookup", - height: 4, - lastAcceptedHeight: 5, - subnetID: ids.GenerateTestID(), - currentPrimaryNetworkValidators: []*validators.Validator{ - copyPrimaryValidator(vdrs[0]), - }, - currentSubnetValidators: []*validators.Validator{ - copySubnetValidator(vdrs[0]), - }, - weightDiffs: []map[ids.NodeID]*state.ValidatorWeightDiff{ - {}, - }, - pkDiffs: []map[ids.NodeID]*bls.PublicKey{ - { - vdrs[1].NodeID: vdrs[1].PublicKey, - }, - }, - expectedVdrSet: map[ids.NodeID]*validators.GetValidatorOutput{ - vdrs[0].NodeID: { - NodeID: vdrs[0].NodeID, - PublicKey: vdrs[0].PublicKey, - Weight: vdrs[0].Weight, - }, - }, - expectedErr: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - // Mock the VM's validators - vdrs := validators.NewMockManager(ctrl) - vm.Validators = vdrs - mockSubnetVdrSet := validators.NewMockSet(ctrl) - mockSubnetVdrSet.EXPECT().List().Return(tt.currentSubnetValidators).AnyTimes() - vdrs.EXPECT().Get(tt.subnetID).Return(mockSubnetVdrSet, true).AnyTimes() - - mockPrimaryVdrSet := mockSubnetVdrSet - if tt.subnetID != constants.PrimaryNetworkID { - mockPrimaryVdrSet = validators.NewMockSet(ctrl) - vdrs.EXPECT().Get(constants.PrimaryNetworkID).Return(mockPrimaryVdrSet, true).AnyTimes() - } - for _, vdr := range tt.currentPrimaryNetworkValidators { - mockPrimaryVdrSet.EXPECT().Get(vdr.NodeID).Return(vdr, true).AnyTimes() - } - - // Mock the block manager - mockManager := blockexecutor.NewMockManager(ctrl) - vm.manager = mockManager - - // Mock the VM's state - mockState := state.NewMockState(ctrl) - vm.state = mockState - - // Tell state what diffs to report - for _, weightDiff := range tt.weightDiffs { - mockState.EXPECT().GetValidatorWeightDiffs(gomock.Any(), gomock.Any()).Return(weightDiff, nil) - } - - for _, pkDiff := range tt.pkDiffs { - mockState.EXPECT().GetValidatorPublicKeyDiffs(gomock.Any()).Return(pkDiff, nil) - } - - // Tell state last accepted block to report - mockTip := smcon.NewMockBlock(ctrl) - mockTip.EXPECT().Height().Return(tt.lastAcceptedHeight) - mockTipID := ids.GenerateTestID() - mockState.EXPECT().GetLastAccepted().Return(mockTipID) - mockManager.EXPECT().GetBlock(mockTipID).Return(mockTip, nil) - - // Compute validator set at previous height - gotVdrSet, err := vm.GetValidatorSet(context.Background(), tt.height, tt.subnetID) - require.ErrorIs(err, tt.expectedErr) - if tt.expectedErr != nil { - return - } - require.Equal(len(tt.expectedVdrSet), len(gotVdrSet)) - for nodeID, vdr := range tt.expectedVdrSet { - otherVdr, ok := gotVdrSet[nodeID] - require.True(ok) - require.Equal(vdr, otherVdr) - } - }) - } - - // Put these back so we don't need to mock calls made on Shutdown - vm.Validators = oldVdrs - vm.state = oldState -} - -func copyPrimaryValidator(vdr *validators.Validator) *validators.Validator { - newVdr := *vdr - return &newVdr -} - -func copySubnetValidator(vdr *validators.Validator) *validators.Validator { - newVdr := *vdr - newVdr.PublicKey = nil - return &newVdr -} - func TestRemovePermissionedValidatorDuringAddPending(t *testing.T) { require := require.New(t) From 629ffd8d0316ceb8e593dcdd6d5fa7ba80bd14b5 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 3 Apr 2023 10:26:45 +0200 Subject: [PATCH 02/16] nit --- vms/platformvm/validators/set.go | 10 ++++++++-- vms/platformvm/vm.go | 7 ++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/vms/platformvm/validators/set.go b/vms/platformvm/validators/set.go index 0d6feb63d84c..342519b8376e 100644 --- a/vms/platformvm/validators/set.go +++ b/vms/platformvm/validators/set.go @@ -36,12 +36,18 @@ var ( ) // P-chain must be able to provide information about validators active -// at different heights. [Set] interface encapsulates all the machinery +// at different heights. [QueribleSet] interface encapsulates all the machinery // to achieve this. -type Set interface { +type QueribleSet interface { validators.State GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) +} + +// Set interface adds to QueribleSet the ability to blocks IDs +// to serve GetMinimumHeight +type Set interface { + QueribleSet Track(blkID ids.ID) } diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index cfc2f26e0ae4..e5b8ea52d8e4 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -62,7 +62,7 @@ var ( type VM struct { config.Config blockbuilder.Builder - pvalidators.Set + pvalidators.QueribleSet metrics metrics.Metrics atomicUtxosManager avax.AtomicUTXOManager @@ -139,7 +139,8 @@ func (vm *VM) Initialize( return err } - vm.Set = pvalidators.NewSet(vm.Config, vm.state, vm.metrics, vm.clock) + validatorsSet := pvalidators.NewSet(vm.Config, vm.state, vm.metrics, vm.clock) + vm.QueribleSet = validatorsSet vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) @@ -178,7 +179,7 @@ func (vm *VM) Initialize( vm.metrics, vm.state, txExecutorBackend, - vm.Set, + validatorsSet, ) vm.Builder = blockbuilder.New( mempool, From e725784f3b347eb4d7b962bf91b71fcf21307526 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Wed, 5 Apr 2023 14:48:31 +0200 Subject: [PATCH 03/16] nits --- vms/platformvm/blocks/executor/acceptor_test.go | 13 ++++++------- vms/platformvm/validators/set_test.go | 9 ++++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/vms/platformvm/blocks/executor/acceptor_test.go b/vms/platformvm/blocks/executor/acceptor_test.go index c90a9f6aa993..b28951556f7a 100644 --- a/vms/platformvm/blocks/executor/acceptor_test.go +++ b/vms/platformvm/blocks/executor/acceptor_test.go @@ -23,9 +23,8 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/validators" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - - pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) func TestAcceptorVisitProposalBlock(t *testing.T) { @@ -62,7 +61,7 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { state: s, }, metrics: metrics.Noop, - validators: &pvalidators.TestSet{}, + validators: &validators.TestSet{}, } err = acceptor.ApricotProposalBlock(blk) @@ -99,7 +98,7 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &pvalidators.TestSet{}, + validators: &validators.TestSet{}, } blk, err := blocks.NewApricotAtomicBlock( @@ -180,7 +179,7 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &pvalidators.TestSet{}, + validators: &validators.TestSet{}, } blk, err := blocks.NewBanffStandardBlock( @@ -271,7 +270,7 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &pvalidators.TestSet{}, + validators: &validators.TestSet{}, bootstrapped: &utils.Atomic[bool]{}, } @@ -357,7 +356,7 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &pvalidators.TestSet{}, + validators: &validators.TestSet{}, bootstrapped: &utils.Atomic[bool]{}, } diff --git a/vms/platformvm/validators/set_test.go b/vms/platformvm/validators/set_test.go index 77b0d9f29a9c..defecfdc7db7 100644 --- a/vms/platformvm/validators/set_test.go +++ b/vms/platformvm/validators/set_test.go @@ -8,6 +8,12 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" + + "github.com/prometheus/client_golang/prometheus" + + "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/chains" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" @@ -23,9 +29,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/metrics" "github.com/ava-labs/avalanchego/vms/platformvm/reward" "github.com/ava-labs/avalanchego/vms/platformvm/state" - "github.com/golang/mock/gomock" - "github.com/prometheus/client_golang/prometheus" - "github.com/stretchr/testify/require" ) // AVAX asset ID in tests From 0c8bc403696305484295626e1cc8791ccbc2e08a Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Wed, 5 Apr 2023 15:10:30 +0200 Subject: [PATCH 04/16] nits --- vms/platformvm/blocks/builder/helpers_test.go | 2 +- vms/platformvm/blocks/executor/acceptor_test.go | 10 +++++----- vms/platformvm/blocks/executor/helpers_test.go | 4 ++-- vms/platformvm/validators/test_set.go | 17 ++++++++--------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/vms/platformvm/blocks/builder/helpers_test.go b/vms/platformvm/blocks/builder/helpers_test.go index b3a872b2abf5..f62a6e70c95b 100644 --- a/vms/platformvm/blocks/builder/helpers_test.go +++ b/vms/platformvm/blocks/builder/helpers_test.go @@ -169,7 +169,7 @@ func newEnvironment(t *testing.T) *environment { metrics, res.state, &res.backend, - &pvalidators.TestSet{}, + pvalidators.TestSet, ) res.Builder = New( diff --git a/vms/platformvm/blocks/executor/acceptor_test.go b/vms/platformvm/blocks/executor/acceptor_test.go index b28951556f7a..b371d27a5bd7 100644 --- a/vms/platformvm/blocks/executor/acceptor_test.go +++ b/vms/platformvm/blocks/executor/acceptor_test.go @@ -61,7 +61,7 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { state: s, }, metrics: metrics.Noop, - validators: &validators.TestSet{}, + validators: validators.TestSet, } err = acceptor.ApricotProposalBlock(blk) @@ -98,7 +98,7 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &validators.TestSet{}, + validators: validators.TestSet, } blk, err := blocks.NewApricotAtomicBlock( @@ -179,7 +179,7 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &validators.TestSet{}, + validators: validators.TestSet, } blk, err := blocks.NewBanffStandardBlock( @@ -270,7 +270,7 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &validators.TestSet{}, + validators: validators.TestSet, bootstrapped: &utils.Atomic[bool]{}, } @@ -356,7 +356,7 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: &validators.TestSet{}, + validators: validators.TestSet, bootstrapped: &utils.Atomic[bool]{}, } diff --git a/vms/platformvm/blocks/executor/helpers_test.go b/vms/platformvm/blocks/executor/helpers_test.go index 8ce1bc537c6a..e3335287ca1d 100644 --- a/vms/platformvm/blocks/executor/helpers_test.go +++ b/vms/platformvm/blocks/executor/helpers_test.go @@ -204,7 +204,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.state, res.backend, - &pvalidators.TestSet{}, + pvalidators.TestSet, ) addSubnet(res) } else { @@ -213,7 +213,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.mockedState, res.backend, - &pvalidators.TestSet{}, + pvalidators.TestSet, ) // we do not add any subnet to state, since we can mock // whatever we need diff --git a/vms/platformvm/validators/test_set.go b/vms/platformvm/validators/test_set.go index 7ec649ced696..7104470999c6 100644 --- a/vms/platformvm/validators/test_set.go +++ b/vms/platformvm/validators/test_set.go @@ -10,28 +10,27 @@ import ( "github.com/ava-labs/avalanchego/snow/validators" ) -var _ validators.State = (*TestSet)(nil) +var TestSet Set = testSet{} -type TestSet struct{} +type testSet struct{} -func (*TestSet) GetMinimumHeight(_ context.Context) (uint64, error) { +func (testSet) GetMinimumHeight(context.Context) (uint64, error) { return 0, nil } -func (*TestSet) GetCurrentHeight(_ context.Context) (uint64, error) { +func (testSet) GetCurrentHeight(context.Context) (uint64, error) { return 0, nil } -func (*TestSet) GetSubnetID(_ context.Context, _ ids.ID) (ids.ID, error) { +func (testSet) GetSubnetID(context.Context, ids.ID) (ids.ID, error) { return ids.Empty, nil } -func (*TestSet) GetValidatorSet(_ context.Context, _ uint64, _ ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { +func (testSet) GetValidatorSet(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { return nil, nil } -func (*TestSet) GetValidatorIDs(_ ids.ID) ([]ids.NodeID, bool) { +func (testSet) GetValidatorIDs(ids.ID) ([]ids.NodeID, bool) { return nil, false } - -func (*TestSet) Track(_ ids.ID) {} +func (testSet) Track(ids.ID) {} From 97bafa46f6df52b53b3f66e9eafe487e8604fa03 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 11 Apr 2023 08:25:06 +0200 Subject: [PATCH 05/16] minor renaming --- vms/platformvm/blocks/builder/helpers_test.go | 2 +- vms/platformvm/blocks/executor/acceptor.go | 2 +- .../blocks/executor/acceptor_test.go | 10 +-- .../blocks/executor/helpers_test.go | 4 +- vms/platformvm/blocks/executor/manager.go | 2 +- .../validators/{set.go => manager.go} | 82 +++++++++---------- .../{set_test.go => manager_test.go} | 2 +- vms/platformvm/validators/test_manager.go | 36 ++++++++ vms/platformvm/validators/test_set.go | 36 -------- vms/platformvm/vm.go | 6 +- 10 files changed, 91 insertions(+), 91 deletions(-) rename vms/platformvm/validators/{set.go => manager.go} (76%) rename vms/platformvm/validators/{set_test.go => manager_test.go} (99%) create mode 100644 vms/platformvm/validators/test_manager.go delete mode 100644 vms/platformvm/validators/test_set.go diff --git a/vms/platformvm/blocks/builder/helpers_test.go b/vms/platformvm/blocks/builder/helpers_test.go index f62a6e70c95b..2ff698da6b17 100644 --- a/vms/platformvm/blocks/builder/helpers_test.go +++ b/vms/platformvm/blocks/builder/helpers_test.go @@ -169,7 +169,7 @@ func newEnvironment(t *testing.T) *environment { metrics, res.state, &res.backend, - pvalidators.TestSet, + pvalidators.TestManager, ) res.Builder = New( diff --git a/vms/platformvm/blocks/executor/acceptor.go b/vms/platformvm/blocks/executor/acceptor.go index e3a313343511..6df280e05600 100644 --- a/vms/platformvm/blocks/executor/acceptor.go +++ b/vms/platformvm/blocks/executor/acceptor.go @@ -24,7 +24,7 @@ var _ blocks.Visitor = (*acceptor)(nil) type acceptor struct { *backend metrics metrics.Metrics - validators validators.Set + validators validators.Manager bootstrapped *utils.Atomic[bool] } diff --git a/vms/platformvm/blocks/executor/acceptor_test.go b/vms/platformvm/blocks/executor/acceptor_test.go index b371d27a5bd7..dcb9fd340505 100644 --- a/vms/platformvm/blocks/executor/acceptor_test.go +++ b/vms/platformvm/blocks/executor/acceptor_test.go @@ -61,7 +61,7 @@ func TestAcceptorVisitProposalBlock(t *testing.T) { state: s, }, metrics: metrics.Noop, - validators: validators.TestSet, + validators: validators.TestManager, } err = acceptor.ApricotProposalBlock(blk) @@ -98,7 +98,7 @@ func TestAcceptorVisitAtomicBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: validators.TestSet, + validators: validators.TestManager, } blk, err := blocks.NewApricotAtomicBlock( @@ -179,7 +179,7 @@ func TestAcceptorVisitStandardBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: validators.TestSet, + validators: validators.TestManager, } blk, err := blocks.NewBanffStandardBlock( @@ -270,7 +270,7 @@ func TestAcceptorVisitCommitBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: validators.TestSet, + validators: validators.TestManager, bootstrapped: &utils.Atomic[bool]{}, } @@ -356,7 +356,7 @@ func TestAcceptorVisitAbortBlock(t *testing.T) { }, }, metrics: metrics.Noop, - validators: validators.TestSet, + validators: validators.TestManager, bootstrapped: &utils.Atomic[bool]{}, } diff --git a/vms/platformvm/blocks/executor/helpers_test.go b/vms/platformvm/blocks/executor/helpers_test.go index e3335287ca1d..b86f340aaccc 100644 --- a/vms/platformvm/blocks/executor/helpers_test.go +++ b/vms/platformvm/blocks/executor/helpers_test.go @@ -204,7 +204,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.state, res.backend, - pvalidators.TestSet, + pvalidators.TestManager, ) addSubnet(res) } else { @@ -213,7 +213,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment { metrics, res.mockedState, res.backend, - pvalidators.TestSet, + pvalidators.TestManager, ) // we do not add any subnet to state, since we can mock // whatever we need diff --git a/vms/platformvm/blocks/executor/manager.go b/vms/platformvm/blocks/executor/manager.go index 17c5b84db70c..5f036261bf16 100644 --- a/vms/platformvm/blocks/executor/manager.go +++ b/vms/platformvm/blocks/executor/manager.go @@ -31,7 +31,7 @@ func NewManager( metrics metrics.Metrics, s state.State, txExecutorBackend *executor.Backend, - validatorsSet validators.Set, + validatorsSet validators.Manager, ) Manager { backend := &backend{ Mempool: mempool, diff --git a/vms/platformvm/validators/set.go b/vms/platformvm/validators/manager.go similarity index 76% rename from vms/platformvm/validators/set.go rename to vms/platformvm/validators/manager.go index 342519b8376e..d51511978734 100644 --- a/vms/platformvm/validators/set.go +++ b/vms/platformvm/validators/manager.go @@ -30,34 +30,34 @@ const ( ) var ( - _ validators.State = (*set)(nil) + _ validators.State = (*manager)(nil) ErrMissingValidatorSet = errors.New("missing validator set") ) // P-chain must be able to provide information about validators active -// at different heights. [QueribleSet] interface encapsulates all the machinery +// at different heights. [QueryManager] interface encapsulates all the machinery // to achieve this. -type QueribleSet interface { +type QueryManager interface { validators.State GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) } -// Set interface adds to QueribleSet the ability to blocks IDs +// Manager interface adds to QueribleSet the ability to blocks IDs // to serve GetMinimumHeight -type Set interface { - QueribleSet +type Manager interface { + QueryManager Track(blkID ids.ID) } -func NewSet( +func NewManager( cfg config.Config, state state.State, metrics metrics.Metrics, clk mockable.Clock, -) Set { - return &set{ +) Manager { + return &manager{ cfg: cfg, state: state, metrics: metrics, @@ -73,7 +73,7 @@ func NewSet( } } -type set struct { +type manager struct { cfg config.Config state state.State metrics metrics.Metrics @@ -105,17 +105,17 @@ type set struct { // If [UseCurrentHeight] is true, we will always return the last accepted block // height as the minimum. This is used to trigger the proposervm on recently // created subnets before [recentlyAcceptedWindowTTL]. -func (vs *set) GetMinimumHeight(ctx context.Context) (uint64, error) { - if vs.cfg.UseCurrentHeight { - return vs.GetCurrentHeight(ctx) +func (m *manager) GetMinimumHeight(ctx context.Context) (uint64, error) { + if m.cfg.UseCurrentHeight { + return m.GetCurrentHeight(ctx) } - oldest, ok := vs.recentlyAccepted.Oldest() + oldest, ok := m.recentlyAccepted.Oldest() if !ok { - return vs.GetCurrentHeight(ctx) + return m.GetCurrentHeight(ctx) } - blk, _, err := vs.state.GetStatelessBlock(oldest) + blk, _, err := m.state.GetStatelessBlock(oldest) if err != nil { return 0, err } @@ -129,9 +129,9 @@ func (vs *set) GetMinimumHeight(ctx context.Context) (uint64, error) { } // GetCurrentHeight returns the height of the last accepted block -func (vs *set) GetCurrentHeight(context.Context) (uint64, error) { - lastAcceptedID := vs.state.GetLastAccepted() - lastAccepted, _, err := vs.state.GetStatelessBlock(lastAcceptedID) +func (m *manager) GetCurrentHeight(context.Context) (uint64, error) { + lastAcceptedID := m.state.GetLastAccepted() + lastAccepted, _, err := m.state.GetStatelessBlock(lastAcceptedID) if err != nil { return 0, err } @@ -140,22 +140,22 @@ func (vs *set) GetCurrentHeight(context.Context) (uint64, error) { // GetValidatorSet returns the validator set at the specified height for the // provided subnetID. -func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - validatorSetsCache, exists := vs.caches[subnetID] +func (m *manager) GetValidatorSet(ctx context.Context, height 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} // Only cache tracked subnets - if subnetID == constants.PrimaryNetworkID || vs.cfg.TrackedSubnets.Contains(subnetID) { - vs.caches[subnetID] = validatorSetsCache + if subnetID == constants.PrimaryNetworkID || m.cfg.TrackedSubnets.Contains(subnetID) { + m.caches[subnetID] = validatorSetsCache } } if validatorSet, ok := validatorSetsCache.Get(height); ok { - vs.metrics.IncValidatorSetsCached() + m.metrics.IncValidatorSetsCached() return validatorSet, nil } - lastAcceptedHeight, err := vs.GetCurrentHeight(ctx) + lastAcceptedHeight, err := m.GetCurrentHeight(ctx) if err != nil { return nil, err } @@ -164,16 +164,16 @@ func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids. } // get the start time to track metrics - startTime := vs.clk.Time() + startTime := m.clk.Time() - currentSubnetValidators, ok := vs.cfg.Validators.Get(subnetID) + currentSubnetValidators, ok := m.cfg.Validators.Get(subnetID) if !ok { currentSubnetValidators = validators.NewSet() - if err := vs.state.ValidatorSet(subnetID, currentSubnetValidators); err != nil { + if err := m.state.ValidatorSet(subnetID, currentSubnetValidators); err != nil { return nil, err } } - currentPrimaryNetworkValidators, ok := vs.cfg.Validators.Get(constants.PrimaryNetworkID) + currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { // This should never happen return nil, ErrMissingValidatorSet @@ -195,7 +195,7 @@ func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids. } for i := lastAcceptedHeight; i > height; i-- { - weightDiffs, err := vs.state.GetValidatorWeightDiffs(i, subnetID) + weightDiffs, err := m.state.GetValidatorWeightDiffs(i, subnetID) if err != nil { return nil, err } @@ -235,7 +235,7 @@ func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids. } } - pkDiffs, err := vs.state.GetValidatorPublicKeyDiffs(i) + pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(i) if err != nil { return nil, err } @@ -255,20 +255,20 @@ func (vs *set) GetValidatorSet(ctx context.Context, height uint64, subnetID ids. // cache the validator set validatorSetsCache.Put(height, vdrSet) - endTime := vs.clk.Time() - vs.metrics.IncValidatorSetsCreated() - vs.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) - vs.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) + endTime := m.clk.Time() + m.metrics.IncValidatorSetsCreated() + m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) + m.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) return vdrSet, nil } // GetCurrentHeight returns the height of the last accepted block -func (vs *set) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { +func (m *manager) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { if chainID == constants.PlatformChainID { return constants.PrimaryNetworkID, nil } - chainTx, _, err := vs.state.GetTx(chainID) + chainTx, _, err := m.state.GetTx(chainID) if err != nil { return ids.Empty, fmt.Errorf( "problem retrieving blockchain %q: %w", @@ -283,8 +283,8 @@ func (vs *set) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { return chain.SubnetID, nil } -func (vs *set) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { - validatorSet, exist := vs.cfg.Validators.Get(subnetID) +func (m *manager) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { + validatorSet, exist := m.cfg.Validators.Get(subnetID) if !exist { return nil, false } @@ -298,6 +298,6 @@ func (vs *set) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { return validatorIDs, true } -func (vs *set) Track(blkID ids.ID) { - vs.recentlyAccepted.Add(blkID) +func (m *manager) Track(blkID ids.ID) { + m.recentlyAccepted.Add(blkID) } diff --git a/vms/platformvm/validators/set_test.go b/vms/platformvm/validators/manager_test.go similarity index 99% rename from vms/platformvm/validators/set_test.go rename to vms/platformvm/validators/manager_test.go index defecfdc7db7..19286ba6a8cb 100644 --- a/vms/platformvm/validators/set_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -385,7 +385,7 @@ func TestVM_GetValidatorSet(t *testing.T) { r.NoError(err) clk := mockable.Clock{} - validatorssSet := NewSet(cfg, mockState, metrics, clk) + validatorssSet := NewManager(cfg, mockState, metrics, clk) // Mock the VM's validators mockSubnetVdrSet := validators.NewMockSet(ctrl) diff --git a/vms/platformvm/validators/test_manager.go b/vms/platformvm/validators/test_manager.go new file mode 100644 index 000000000000..ae310bb0e56d --- /dev/null +++ b/vms/platformvm/validators/test_manager.go @@ -0,0 +1,36 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package validators + +import ( + "context" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/validators" +) + +var TestManager Manager = testManager{} + +type testManager struct{} + +func (testManager) GetMinimumHeight(context.Context) (uint64, error) { + return 0, nil +} + +func (testManager) GetCurrentHeight(context.Context) (uint64, error) { + return 0, nil +} + +func (testManager) GetSubnetID(context.Context, ids.ID) (ids.ID, error) { + return ids.Empty, nil +} + +func (testManager) GetValidatorSet(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return nil, nil +} + +func (testManager) GetValidatorIDs(ids.ID) ([]ids.NodeID, bool) { + return nil, false +} +func (testManager) Track(ids.ID) {} diff --git a/vms/platformvm/validators/test_set.go b/vms/platformvm/validators/test_set.go deleted file mode 100644 index 7104470999c6..000000000000 --- a/vms/platformvm/validators/test_set.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package validators - -import ( - "context" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" -) - -var TestSet Set = testSet{} - -type testSet struct{} - -func (testSet) GetMinimumHeight(context.Context) (uint64, error) { - return 0, nil -} - -func (testSet) GetCurrentHeight(context.Context) (uint64, error) { - return 0, nil -} - -func (testSet) GetSubnetID(context.Context, ids.ID) (ids.ID, error) { - return ids.Empty, nil -} - -func (testSet) GetValidatorSet(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return nil, nil -} - -func (testSet) GetValidatorIDs(ids.ID) ([]ids.NodeID, bool) { - return nil, false -} -func (testSet) Track(ids.ID) {} diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index e5b8ea52d8e4..c04781bc921f 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -62,7 +62,7 @@ var ( type VM struct { config.Config blockbuilder.Builder - pvalidators.QueribleSet + pvalidators.QueryManager metrics metrics.Metrics atomicUtxosManager avax.AtomicUTXOManager @@ -139,8 +139,8 @@ func (vm *VM) Initialize( return err } - validatorsSet := pvalidators.NewSet(vm.Config, vm.state, vm.metrics, vm.clock) - vm.QueribleSet = validatorsSet + validatorsSet := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, vm.clock) + vm.QueryManager = validatorsSet vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) From d9b9452254153bb4bf47f058fe1ffbaae396d32f Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Tue, 11 Apr 2023 08:50:11 +0200 Subject: [PATCH 06/16] clock cleanup --- vms/platformvm/blocks/builder/helpers_test.go | 4 ++-- vms/platformvm/blocks/executor/helpers_test.go | 4 ++-- vms/platformvm/txs/executor/helpers_test.go | 14 +++++++------- vms/platformvm/validators/manager.go | 6 +++--- vms/platformvm/validators/manager_test.go | 2 +- vms/platformvm/vm.go | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/vms/platformvm/blocks/builder/helpers_test.go b/vms/platformvm/blocks/builder/helpers_test.go index 2ff698da6b17..0e5415548fa9 100644 --- a/vms/platformvm/blocks/builder/helpers_test.go +++ b/vms/platformvm/blocks/builder/helpers_test.go @@ -323,9 +323,9 @@ func defaultConfig() *config.Config { func defaultClock() *mockable.Clock { // set time after Banff fork (and before default nextStakerTime) - clk := mockable.Clock{} + clk := &mockable.Clock{} clk.Set(defaultGenesisTime) - return &clk + return clk } type fxVMInt struct { diff --git a/vms/platformvm/blocks/executor/helpers_test.go b/vms/platformvm/blocks/executor/helpers_test.go index b86f340aaccc..b94bf7379858 100644 --- a/vms/platformvm/blocks/executor/helpers_test.go +++ b/vms/platformvm/blocks/executor/helpers_test.go @@ -354,9 +354,9 @@ func defaultConfig() *config.Config { } func defaultClock() *mockable.Clock { - clk := mockable.Clock{} + clk := &mockable.Clock{} clk.Set(defaultGenesisTime) - return &clk + return clk } type fxVMInt struct { diff --git a/vms/platformvm/txs/executor/helpers_test.go b/vms/platformvm/txs/executor/helpers_test.go index 4921977b5364..7c3c63ecb8e2 100644 --- a/vms/platformvm/txs/executor/helpers_test.go +++ b/vms/platformvm/txs/executor/helpers_test.go @@ -123,19 +123,19 @@ func newEnvironment(postBanff, postCortina bool) *environment { baseDB := versiondb.New(baseDBManager.Current().Database) ctx, msm := defaultCtx(baseDB) - fx := defaultFx(&clk, ctx.Log, isBootstrapped.Get()) + fx := defaultFx(clk, ctx.Log, isBootstrapped.Get()) rewards := reward.NewCalculator(config.RewardConfig) baseState := defaultState(&config, ctx, baseDB, rewards) atomicUTXOs := avax.NewAtomicUTXOManager(ctx.SharedMemory, txs.Codec) uptimes := uptime.NewManager(baseState) - utxoHandler := utxo.NewHandler(ctx, &clk, fx) + utxoHandler := utxo.NewHandler(ctx, clk, fx) txBuilder := builder.New( ctx, &config, - &clk, + clk, fx, baseState, atomicUTXOs, @@ -145,7 +145,7 @@ func newEnvironment(postBanff, postCortina bool) *environment { backend := Backend{ Config: &config, Ctx: ctx, - Clk: &clk, + Clk: clk, Bootstrapped: &isBootstrapped, Fx: fx, FlowChecker: utxoHandler, @@ -156,7 +156,7 @@ func newEnvironment(postBanff, postCortina bool) *environment { env := &environment{ isBootstrapped: &isBootstrapped, config: &config, - clk: &clk, + clk: clk, baseDB: baseDB, ctx: ctx, msm: msm, @@ -321,13 +321,13 @@ func defaultConfig(postBanff, postCortina bool) config.Config { } } -func defaultClock(postFork bool) mockable.Clock { +func defaultClock(postFork bool) *mockable.Clock { now := defaultGenesisTime if postFork { // 1 second after Banff fork now = defaultValidateEndTime.Add(-2 * time.Second) } - clk := mockable.Clock{} + clk := &mockable.Clock{} clk.Set(now) return clk } diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index d51511978734..572d8b3b98a8 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -55,17 +55,17 @@ func NewManager( cfg config.Config, state state.State, metrics metrics.Metrics, - clk mockable.Clock, + clk *mockable.Clock, ) Manager { return &manager{ cfg: cfg, state: state, metrics: metrics, - clk: &clk, + clk: clk, caches: make(map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]), recentlyAccepted: window.New[ids.ID]( window.Config{ - Clock: &clk, + Clock: clk, MaxSize: maxRecentlyAcceptedWindowSize, TTL: recentlyAcceptedWindowTTL, }, diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 19286ba6a8cb..adc33d52fb39 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -384,7 +384,7 @@ func TestVM_GetValidatorSet(t *testing.T) { metrics, err := metrics.New("", prometheus.NewRegistry(), cfg.TrackedSubnets) r.NoError(err) - clk := mockable.Clock{} + clk := &mockable.Clock{} validatorssSet := NewManager(cfg, mockState, metrics, clk) // Mock the VM's validators diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index c04781bc921f..671fbfb422b1 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -139,7 +139,7 @@ func (vm *VM) Initialize( return err } - validatorsSet := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, vm.clock) + validatorsSet := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) vm.QueryManager = validatorsSet vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) From ee25500b954366128e2b842d7ededf78102a89a8 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Thu, 4 May 2023 12:37:53 +0200 Subject: [PATCH 07/16] nits --- vms/platformvm/blocks/executor/acceptor.go | 2 +- vms/platformvm/validators/manager.go | 22 ++++++++++++---------- vms/platformvm/validators/manager_test.go | 9 ++++----- vms/platformvm/validators/test_manager.go | 2 +- vms/platformvm/vm.go | 6 +++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/vms/platformvm/blocks/executor/acceptor.go b/vms/platformvm/blocks/executor/acceptor.go index bee4edc7f303..54adc602545e 100644 --- a/vms/platformvm/blocks/executor/acceptor.go +++ b/vms/platformvm/blocks/executor/acceptor.go @@ -315,6 +315,6 @@ func (a *acceptor) commonAccept(b blocks.Block) error { a.state.SetLastAccepted(blkID) a.state.SetHeight(b.Height()) a.state.AddStatelessBlock(b, choices.Accepted) - a.validators.Track(blkID) + a.validators.OnAcceptedBlockID(blkID) return nil } diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 572d8b3b98a8..02ddfabb2b8f 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -35,20 +35,22 @@ var ( ErrMissingValidatorSet = errors.New("missing validator set") ) -// P-chain must be able to provide information about validators active -// at different heights. [QueryManager] interface encapsulates all the machinery -// to achieve this. +// QueryManager encapsulates the logic that allows the P-chain to provide +// information about validators active at different heights. type QueryManager interface { validators.State GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) } -// Manager interface adds to QueribleSet the ability to blocks IDs +// Manager interface adds to QueryManager the ability to blocks IDs // to serve GetMinimumHeight type Manager interface { QueryManager - Track(blkID ids.ID) + + // OnAcceptedBlockID register the ID of the latest accepted block. + // It is used to update [recentlyAccepted] sliding window. + OnAcceptedBlockID(blkID ids.ID) } func NewManager( @@ -102,9 +104,9 @@ type manager struct { // height which is likely (but not guaranteed) to also be older than the // window's configured TTL. // -// If [UseCurrentHeight] is true, we will always return the last accepted block -// height as the minimum. This is used to trigger the proposervm on recently -// created subnets before [recentlyAcceptedWindowTTL]. +// If [UseCurrentHeight] is true, we override the block selection policy +// described above and we will always return the last accepted block height +// as the minimum. func (m *manager) GetMinimumHeight(ctx context.Context) (uint64, error) { if m.cfg.UseCurrentHeight { return m.GetCurrentHeight(ctx) @@ -262,7 +264,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i return vdrSet, nil } -// GetCurrentHeight returns the height of the last accepted block +// GetSubnetID returns subnetID of the specified chainID func (m *manager) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { if chainID == constants.PlatformChainID { return constants.PrimaryNetworkID, nil @@ -298,6 +300,6 @@ func (m *manager) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { return validatorIDs, true } -func (m *manager) Track(blkID ids.ID) { +func (m *manager) OnAcceptedBlockID(blkID ids.ID) { m.recentlyAccepted.Add(blkID) } diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index adc33d52fb39..3b473014483f 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -40,9 +40,6 @@ var defaultRewardConfig = reward.Config{ } func TestVM_GetValidatorSet(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - // Populate the validator set to use below var ( numVdrs = 4 @@ -71,10 +68,10 @@ func TestVM_GetValidatorSet(t *testing.T) { currentPrimaryNetworkValidators []*validators.Validator currentSubnetValidators []*validators.Validator // Diff at tip, block before tip, etc. - // This must have [height] - [lastAcceptedHeight] elements + // This must have [lastAcceptedHeight] - [height] elements weightDiffs []map[ids.NodeID]*state.ValidatorWeightDiff // Diff at tip, block before tip, etc. - // This must have [height] - [lastAcceptedHeight] elements + // This must have [lastAcceptedHeight] - [height] elements pkDiffs []map[ids.NodeID]*bls.PublicKey expectedVdrSet map[ids.NodeID]*validators.GetValidatorOutput expectedErr error @@ -368,6 +365,8 @@ func TestVM_GetValidatorSet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() // setup validators set vdrs := validators.NewMockManager(ctrl) diff --git a/vms/platformvm/validators/test_manager.go b/vms/platformvm/validators/test_manager.go index ae310bb0e56d..b0c547f52a69 100644 --- a/vms/platformvm/validators/test_manager.go +++ b/vms/platformvm/validators/test_manager.go @@ -33,4 +33,4 @@ func (testManager) GetValidatorSet(context.Context, uint64, ids.ID) (map[ids.Nod func (testManager) GetValidatorIDs(ids.ID) ([]ids.NodeID, bool) { return nil, false } -func (testManager) Track(ids.ID) {} +func (testManager) OnAcceptedBlockID(ids.ID) {} diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index 671fbfb422b1..f6cb76c57b9f 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -139,8 +139,8 @@ func (vm *VM) Initialize( return err } - validatorsSet := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) - vm.QueryManager = validatorsSet + queryManager := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) + vm.QueryManager = queryManager vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) @@ -179,7 +179,7 @@ func (vm *VM) Initialize( vm.metrics, vm.state, txExecutorBackend, - validatorsSet, + queryManager, ) vm.Builder = blockbuilder.New( mempool, From 42b1dd55390c1367aa60f1d619d319d370dcef82 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Thu, 4 May 2023 12:59:49 +0200 Subject: [PATCH 08/16] cleanup --- vms/platformvm/validators/manager.go | 109 +++++++++++++++------------ 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 02ddfabb2b8f..74b277a3c0d0 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -32,6 +32,7 @@ const ( var ( _ validators.State = (*manager)(nil) + ErrMissingValidator = errors.New("missing validator") ErrMissingValidatorSet = errors.New("missing validator set") ) @@ -178,7 +179,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i currentPrimaryNetworkValidators, ok := m.cfg.Validators.Get(constants.PrimaryNetworkID) if !ok { // This should never happen - return nil, ErrMissingValidatorSet + return nil, ErrMissingValidator } currentSubnetValidatorList := currentSubnetValidators.List() @@ -187,7 +188,7 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i primaryVdr, ok := currentPrimaryNetworkValidators.Get(vdr.NodeID) if !ok { // This should never happen - return nil, fmt.Errorf("%w: %s", ErrMissingValidatorSet, vdr.NodeID) + return nil, fmt.Errorf("%w: %s", ErrMissingValidator, vdr.NodeID) } vdrSet[vdr.NodeID] = &validators.GetValidatorOutput{ NodeID: vdr.NodeID, @@ -197,71 +198,79 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } for i := lastAcceptedHeight; i > height; i-- { - weightDiffs, err := m.state.GetValidatorWeightDiffs(i, subnetID) + err := m.applyValidatorDiffs(vdrSet, subnetID, height) if err != nil { return nil, 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 - } + // cache the validator set + validatorSetsCache.Put(height, vdrSet) - // The weight of this node changed at this block. - var op func(uint64, uint64) (uint64, error) - if weightDiff.Decrease { - // The validator's weight was decreased at this block, so in the - // prior block it was higher. - op = math.Add64 - } else { - // The validator's weight was increased at this block, so in the - // prior block it was lower. - op = math.Sub[uint64] - } + endTime := m.clk.Time() + m.metrics.IncValidatorSetsCreated() + m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) + m.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) + return vdrSet, nil +} - // Apply the weight change. - vdr.Weight, err = op(vdr.Weight, weightDiff.Amount) - if err != nil { - return nil, err - } +func (m *manager) applyValidatorDiffs( + vdrSet map[ids.NodeID]*validators.GetValidatorOutput, + subnetID ids.ID, + height uint64, +) error { + weightDiffs, err := m.state.GetValidatorWeightDiffs(height, subnetID) + 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) + 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 } - pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(i) + // 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) + } if err != nil { - return nil, err + 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 { - // The validator's public key was removed at this block, so it - // was in the validator set before. - vdr.PublicKey = pk - } + if vdr.Weight == 0 { + // The validator's weight was 0 before this block so + // they weren't in the validator set. + delete(vdrSet, nodeID) } } - // cache the validator set - validatorSetsCache.Put(height, vdrSet) + pkDiffs, err := m.state.GetValidatorPublicKeyDiffs(height) + if err != nil { + return err + } - endTime := m.clk.Time() - m.metrics.IncValidatorSetsCreated() - m.metrics.AddValidatorSetsDuration(endTime.Sub(startTime)) - m.metrics.AddValidatorSetsHeightDiff(lastAcceptedHeight - height) - return vdrSet, nil + 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 { + // The validator's public key was removed at this block, so it + // was in the validator set before. + vdr.PublicKey = pk + } + } + return nil } // GetSubnetID returns subnetID of the specified chainID From a736dfcc20a03f35b41e6ad9070df401078bd35a Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Thu, 4 May 2023 13:17:20 +0200 Subject: [PATCH 09/16] fixed cleanup --- vms/platformvm/validators/manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 74b277a3c0d0..0e7a6668a6f8 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -197,8 +197,8 @@ func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID i } } - for i := lastAcceptedHeight; i > height; i-- { - err := m.applyValidatorDiffs(vdrSet, subnetID, height) + for diffHeight := lastAcceptedHeight; diffHeight > height; diffHeight-- { + err := m.applyValidatorDiffs(vdrSet, subnetID, diffHeight) if err != nil { return nil, err } From cb1eafbcedd8b0ff8473a92e90d3978e495c1bd1 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Thu, 11 May 2023 08:28:06 +0200 Subject: [PATCH 10/16] nits --- vms/platformvm/validators/manager.go | 2 +- vms/platformvm/validators/manager_test.go | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 0e7a6668a6f8..907a76f8b8bd 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -49,7 +49,7 @@ type QueryManager interface { type Manager interface { QueryManager - // OnAcceptedBlockID register the ID of the latest accepted block. + // OnAcceptedBlockID registers the ID of the latest accepted block. // It is used to update [recentlyAccepted] sliding window. OnAcceptedBlockID(blkID ids.ID) } diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 3b473014483f..7addff759fb0 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -422,12 +422,7 @@ func TestVM_GetValidatorSet(t *testing.T) { if tt.expectedErr != nil { return } - r.Equal(len(tt.expectedVdrSet), len(gotVdrSet)) - for nodeID, vdr := range tt.expectedVdrSet { - otherVdr, ok := gotVdrSet[nodeID] - r.True(ok) - r.Equal(vdr, otherVdr) - } + r.Equal(tt.expectedVdrSet, gotVdrSet) }) } } From 330e83adf5c27fb7014a2cb48afb7f1d5f0972ac Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:08:28 -0400 Subject: [PATCH 11/16] fix merge --- vms/platformvm/blocks/builder/helpers_test.go | 5 +---- vms/platformvm/blocks/executor/helpers_test.go | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/vms/platformvm/blocks/builder/helpers_test.go b/vms/platformvm/blocks/builder/helpers_test.go index a512bb0a900c..cea4a297a260 100644 --- a/vms/platformvm/blocks/builder/helpers_test.go +++ b/vms/platformvm/blocks/builder/helpers_test.go @@ -55,10 +55,7 @@ import ( pvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators" ) -const ( - testNetworkID = 10 // To be used in tests - defaultWeight = 10000 -) +const defaultWeight = 10000 var ( defaultMinStakingDuration = 24 * time.Hour diff --git a/vms/platformvm/blocks/executor/helpers_test.go b/vms/platformvm/blocks/executor/helpers_test.go index ff7f55e2cc0a..329aa9cc8a5f 100644 --- a/vms/platformvm/blocks/executor/helpers_test.go +++ b/vms/platformvm/blocks/executor/helpers_test.go @@ -60,7 +60,6 @@ const ( pending stakerStatus = iota current - testNetworkID = 10 // To be used in tests defaultWeight = 10000 ) From 79262a52a8325ed44dc75c80045e44ec733cf35a Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:12:00 -0400 Subject: [PATCH 12/16] nit rename --- vms/platformvm/blocks/executor/manager.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vms/platformvm/blocks/executor/manager.go b/vms/platformvm/blocks/executor/manager.go index 5f036261bf16..6343217e45d5 100644 --- a/vms/platformvm/blocks/executor/manager.go +++ b/vms/platformvm/blocks/executor/manager.go @@ -31,7 +31,7 @@ func NewManager( metrics metrics.Metrics, s state.State, txExecutorBackend *executor.Backend, - validatorsSet validators.Manager, + validatorManager validators.Manager, ) Manager { backend := &backend{ Mempool: mempool, @@ -50,7 +50,7 @@ func NewManager( acceptor: &acceptor{ backend: backend, metrics: metrics, - validators: validatorsSet, + validators: validatorManager, bootstrapped: txExecutorBackend.Bootstrapped, }, rejector: &rejector{backend: backend}, From d55469beb3525b57869e5b1be7769551166f6e1c Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:23:29 -0400 Subject: [PATCH 13/16] nit rename --- vms/platformvm/validators/manager.go | 2 +- vms/platformvm/vm.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 907a76f8b8bd..ed3140f95265 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -50,7 +50,7 @@ type Manager interface { QueryManager // OnAcceptedBlockID registers the ID of the latest accepted block. - // It is used to update [recentlyAccepted] sliding window. + // It is used to update the [recentlyAccepted] sliding window. OnAcceptedBlockID(blkID ids.ID) } diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index f6cb76c57b9f..282d58b89761 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -139,8 +139,8 @@ func (vm *VM) Initialize( return err } - queryManager := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) - vm.QueryManager = queryManager + validatorManager := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) + vm.QueryManager = validatorManager vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) @@ -179,7 +179,7 @@ func (vm *VM) Initialize( vm.metrics, vm.state, txExecutorBackend, - queryManager, + validatorManager, ) vm.Builder = blockbuilder.New( mempool, From cfbd56fb06de4312ff48bb44f307c651751eef19 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:29:27 -0400 Subject: [PATCH 14/16] remove duplicate comments --- vms/platformvm/validators/manager.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index ed3140f95265..235b66df9feb 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -141,8 +141,6 @@ func (m *manager) GetCurrentHeight(context.Context) (uint64, error) { return lastAccepted.Height(), nil } -// GetValidatorSet returns the validator set at the specified height for the -// provided subnetID. func (m *manager) GetValidatorSet(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { validatorSetsCache, exists := m.caches[subnetID] if !exists { @@ -273,7 +271,6 @@ func (m *manager) applyValidatorDiffs( return nil } -// GetSubnetID returns subnetID of the specified chainID func (m *manager) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) { if chainID == constants.PlatformChainID { return constants.PrimaryNetworkID, nil From dcc6aa74623d34b2b5f8952bdf0b7d10815ac230 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:50:19 -0400 Subject: [PATCH 15/16] minimize interface size --- snow/validators/manager.go | 14 ++++++++++ vms/platformvm/service.go | 3 +-- vms/platformvm/validators/manager.go | 33 +++-------------------- vms/platformvm/validators/test_manager.go | 3 --- vms/platformvm/vm.go | 33 ++++++++++++----------- 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/snow/validators/manager.go b/snow/validators/manager.go index 0d0bc56372e7..9dd81abffc7a 100644 --- a/snow/validators/manager.go +++ b/snow/validators/manager.go @@ -145,3 +145,17 @@ func Contains(m Manager, subnetID ids.ID, nodeID ids.NodeID) bool { } return vdrs.Contains(nodeID) } + +func NodeIDs(m Manager, subnetID ids.ID) ([]ids.NodeID, error) { + vdrs, exist := m.Get(subnetID) + if !exist { + return nil, fmt.Errorf("%w: %s", errMissingValidators, subnetID) + } + + vdrsList := vdrs.List() + nodeIDs := make([]ids.NodeID, len(vdrsList)) + for i, vdr := range vdrsList { + nodeIDs[i] = vdr.NodeID + } + return nodeIDs, nil +} diff --git a/vms/platformvm/service.go b/vms/platformvm/service.go index d8b26b0779d6..42c2754511a2 100644 --- a/vms/platformvm/service.go +++ b/vms/platformvm/service.go @@ -40,7 +40,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/platformvm/txs/builder" "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor" - "github.com/ava-labs/avalanchego/vms/platformvm/validators" "github.com/ava-labs/avalanchego/vms/secp256k1fx" platformapi "github.com/ava-labs/avalanchego/vms/platformvm/api" @@ -2413,7 +2412,7 @@ func (s *Service) GetTotalStake(_ *http.Request, args *GetTotalStakeArgs, reply vdrs, ok := s.vm.Validators.Get(args.SubnetID) if !ok { - return validators.ErrMissingValidatorSet + return errMissingValidatorSet } weight := json.Uint64(vdrs.Weight()) reply.Weight = weight diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index 235b66df9feb..e88a3afc55f6 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -32,22 +32,13 @@ const ( var ( _ validators.State = (*manager)(nil) - ErrMissingValidator = errors.New("missing validator") - ErrMissingValidatorSet = errors.New("missing validator set") + ErrMissingValidator = errors.New("missing validator") ) -// QueryManager encapsulates the logic that allows the P-chain to provide -// information about validators active at different heights. -type QueryManager interface { - validators.State - - GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) -} - -// Manager interface adds to QueryManager the ability to blocks IDs -// to serve GetMinimumHeight +// Manager adds the ability to introduce newly acceted blocks IDs to the State +// interface. type Manager interface { - QueryManager + validators.State // OnAcceptedBlockID registers the ID of the latest accepted block. // It is used to update the [recentlyAccepted] sliding window. @@ -131,7 +122,6 @@ func (m *manager) GetMinimumHeight(ctx context.Context) (uint64, error) { return blk.Height() - 1, nil } -// GetCurrentHeight returns the height of the last accepted block func (m *manager) GetCurrentHeight(context.Context) (uint64, error) { lastAcceptedID := m.state.GetLastAccepted() lastAccepted, _, err := m.state.GetStatelessBlock(lastAcceptedID) @@ -291,21 +281,6 @@ func (m *manager) GetSubnetID(_ context.Context, chainID ids.ID) (ids.ID, error) return chain.SubnetID, nil } -func (m *manager) GetValidatorIDs(subnetID ids.ID) ([]ids.NodeID, bool) { - validatorSet, exist := m.cfg.Validators.Get(subnetID) - if !exist { - return nil, false - } - validators := validatorSet.List() - - validatorIDs := make([]ids.NodeID, len(validators)) - for i, vdr := range validators { - validatorIDs[i] = vdr.NodeID - } - - return validatorIDs, true -} - func (m *manager) OnAcceptedBlockID(blkID ids.ID) { m.recentlyAccepted.Add(blkID) } diff --git a/vms/platformvm/validators/test_manager.go b/vms/platformvm/validators/test_manager.go index b0c547f52a69..a9ab7254cfd7 100644 --- a/vms/platformvm/validators/test_manager.go +++ b/vms/platformvm/validators/test_manager.go @@ -30,7 +30,4 @@ func (testManager) GetValidatorSet(context.Context, uint64, ids.ID) (map[ids.Nod return nil, nil } -func (testManager) GetValidatorIDs(ids.ID) ([]ids.NodeID, bool) { - return nil, false -} func (testManager) OnAcceptedBlockID(ids.ID) {} diff --git a/vms/platformvm/vm.go b/vms/platformvm/vm.go index 282d58b89761..a55b8a16de6b 100644 --- a/vms/platformvm/vm.go +++ b/vms/platformvm/vm.go @@ -5,6 +5,7 @@ package platformvm import ( "context" + "errors" "fmt" "github.com/gorilla/rpc/v2" @@ -57,12 +58,14 @@ var ( _ secp256k1fx.VM = (*VM)(nil) _ validators.State = (*VM)(nil) _ validators.SubnetConnector = (*VM)(nil) + + errMissingValidatorSet = errors.New("missing validator set") ) type VM struct { config.Config blockbuilder.Builder - pvalidators.QueryManager + validators.State metrics metrics.Metrics atomicUtxosManager avax.AtomicUTXOManager @@ -140,7 +143,7 @@ func (vm *VM) Initialize( } validatorManager := pvalidators.NewManager(vm.Config, vm.state, vm.metrics, &vm.clock) - vm.QueryManager = validatorManager + vm.State = validatorManager vm.atomicUtxosManager = avax.NewAtomicUTXOManager(chainCtx.SharedMemory, txs.Codec) utxoHandler := utxo.NewHandler(vm.ctx, &vm.clock, vm.fx) vm.uptimeManager = uptime.NewManager(vm.state) @@ -264,18 +267,18 @@ func (vm *VM) onNormalOperationsStarted() error { return err } - primaryVdrIDs, exists := vm.GetValidatorIDs(constants.PrimaryNetworkID) - if !exists { - return pvalidators.ErrMissingValidatorSet + primaryVdrIDs, err := validators.NodeIDs(vm.Validators, constants.PrimaryNetworkID) + if err != nil { + return err } if err := vm.uptimeManager.StartTracking(primaryVdrIDs, constants.PrimaryNetworkID); err != nil { return err } for subnetID := range vm.TrackedSubnets { - vdrIDs, exists := vm.GetValidatorIDs(subnetID) - if !exists { - return pvalidators.ErrMissingValidatorSet + vdrIDs, err := validators.NodeIDs(vm.Validators, subnetID) + if err != nil { + return err } if err := vm.uptimeManager.StartTracking(vdrIDs, subnetID); err != nil { return err @@ -311,18 +314,18 @@ func (vm *VM) Shutdown(context.Context) error { vm.Builder.Shutdown() if vm.bootstrapped.Get() { - primaryVdrIDs, exists := vm.GetValidatorIDs(constants.PrimaryNetworkID) - if !exists { - return pvalidators.ErrMissingValidatorSet + primaryVdrIDs, err := validators.NodeIDs(vm.Validators, constants.PrimaryNetworkID) + if err != nil { + return err } if err := vm.uptimeManager.StopTracking(primaryVdrIDs, constants.PrimaryNetworkID); err != nil { return err } for subnetID := range vm.TrackedSubnets { - vdrIDs, exists := vm.GetValidatorIDs(subnetID) - if !exists { - return pvalidators.ErrMissingValidatorSet + vdrIDs, err := validators.NodeIDs(vm.Validators, subnetID) + if err != nil { + return err } if err := vm.uptimeManager.StopTracking(vdrIDs, subnetID); err != nil { return err @@ -451,7 +454,7 @@ func (vm *VM) Logger() logging.Logger { func (vm *VM) getPercentConnected(subnetID ids.ID) (float64, error) { vdrSet, exists := vm.Validators.Get(subnetID) if !exists { - return 0, pvalidators.ErrMissingValidatorSet + return 0, errMissingValidatorSet } vdrSetWeight := vdrSet.Weight() From bb3be27765fe3ee5683fe33a42ea7922996e9399 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 18 May 2023 12:53:42 -0400 Subject: [PATCH 16/16] license --- vms/platformvm/validators/manager.go | 2 +- vms/platformvm/validators/manager_test.go | 2 +- vms/platformvm/validators/test_manager.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vms/platformvm/validators/manager.go b/vms/platformvm/validators/manager.go index e88a3afc55f6..7ded28c7c4e5 100644 --- a/vms/platformvm/validators/manager.go +++ b/vms/platformvm/validators/manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/vms/platformvm/validators/manager_test.go b/vms/platformvm/validators/manager_test.go index 7addff759fb0..7f5d8975f427 100644 --- a/vms/platformvm/validators/manager_test.go +++ b/vms/platformvm/validators/manager_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators diff --git a/vms/platformvm/validators/test_manager.go b/vms/platformvm/validators/test_manager.go index a9ab7254cfd7..d7ffe993248e 100644 --- a/vms/platformvm/validators/test_manager.go +++ b/vms/platformvm/validators/test_manager.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package validators