github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/validators/manager_benchmark_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package validators
     5  
     6  import (
     7  	"context"
     8  	"math/rand"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/MetalBlockchain/metalgo/database/leveldb"
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/snow"
    18  	"github.com/MetalBlockchain/metalgo/snow/validators"
    19  	"github.com/MetalBlockchain/metalgo/utils/constants"
    20  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    21  	"github.com/MetalBlockchain/metalgo/utils/formatting"
    22  	"github.com/MetalBlockchain/metalgo/utils/formatting/address"
    23  	"github.com/MetalBlockchain/metalgo/utils/json"
    24  	"github.com/MetalBlockchain/metalgo/utils/logging"
    25  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    26  	"github.com/MetalBlockchain/metalgo/utils/units"
    27  	"github.com/MetalBlockchain/metalgo/vms/platformvm/api"
    28  	"github.com/MetalBlockchain/metalgo/vms/platformvm/block"
    29  	"github.com/MetalBlockchain/metalgo/vms/platformvm/config"
    30  	"github.com/MetalBlockchain/metalgo/vms/platformvm/metrics"
    31  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    32  	"github.com/MetalBlockchain/metalgo/vms/platformvm/state"
    33  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    34  )
    35  
    36  // BenchmarkGetValidatorSet generates 10k diffs and calculates the time to
    37  // generate the genesis validator set by applying them.
    38  //
    39  // This generates a single diff for each height. In practice there could be
    40  // multiple or zero diffs at a given height.
    41  //
    42  // Note: BenchmarkGetValidatorSet gets the validator set of a subnet rather than
    43  // the primary network because the primary network performs caching that would
    44  // interfere with the benchmark.
    45  func BenchmarkGetValidatorSet(b *testing.B) {
    46  	require := require.New(b)
    47  
    48  	db, err := leveldb.New(
    49  		b.TempDir(),
    50  		nil,
    51  		logging.NoLog{},
    52  		prometheus.NewRegistry(),
    53  	)
    54  	require.NoError(err)
    55  	defer func() {
    56  		require.NoError(db.Close())
    57  	}()
    58  
    59  	avaxAssetID := ids.GenerateTestID()
    60  	genesisTime := time.Now().Truncate(time.Second)
    61  	genesisEndTime := genesisTime.Add(28 * 24 * time.Hour)
    62  
    63  	addr, err := address.FormatBech32(constants.UnitTestHRP, ids.GenerateTestShortID().Bytes())
    64  	require.NoError(err)
    65  
    66  	genesisValidators := []api.GenesisPermissionlessValidator{{
    67  		GenesisValidator: api.GenesisValidator{
    68  			StartTime: json.Uint64(genesisTime.Unix()),
    69  			EndTime:   json.Uint64(genesisEndTime.Unix()),
    70  			NodeID:    ids.GenerateTestNodeID(),
    71  		},
    72  		RewardOwner: &api.Owner{
    73  			Threshold: 1,
    74  			Addresses: []string{addr},
    75  		},
    76  		Staked: []api.UTXO{{
    77  			Amount:  json.Uint64(2 * units.KiloAvax),
    78  			Address: addr,
    79  		}},
    80  		DelegationFee: reward.PercentDenominator,
    81  	}}
    82  
    83  	buildGenesisArgs := api.BuildGenesisArgs{
    84  		NetworkID:     json.Uint32(constants.UnitTestID),
    85  		AvaxAssetID:   avaxAssetID,
    86  		UTXOs:         nil,
    87  		Validators:    genesisValidators,
    88  		Chains:        nil,
    89  		Time:          json.Uint64(genesisTime.Unix()),
    90  		InitialSupply: json.Uint64(360 * units.MegaAvax),
    91  		Encoding:      formatting.Hex,
    92  	}
    93  
    94  	buildGenesisResponse := api.BuildGenesisReply{}
    95  	platformvmSS := api.StaticService{}
    96  	require.NoError(platformvmSS.BuildGenesis(nil, &buildGenesisArgs, &buildGenesisResponse))
    97  
    98  	genesisBytes, err := formatting.Decode(buildGenesisResponse.Encoding, buildGenesisResponse.Bytes)
    99  	require.NoError(err)
   100  
   101  	vdrs := validators.NewManager()
   102  
   103  	execConfig, err := config.GetExecutionConfig(nil)
   104  	require.NoError(err)
   105  
   106  	metrics, err := metrics.New(prometheus.NewRegistry())
   107  	require.NoError(err)
   108  
   109  	s, err := state.New(
   110  		db,
   111  		genesisBytes,
   112  		prometheus.NewRegistry(),
   113  		&config.Config{
   114  			Validators: vdrs,
   115  		},
   116  		execConfig,
   117  		&snow.Context{
   118  			NetworkID: constants.UnitTestID,
   119  			NodeID:    ids.GenerateTestNodeID(),
   120  			Log:       logging.NoLog{},
   121  		},
   122  		metrics,
   123  		reward.NewCalculator(reward.Config{
   124  			MaxConsumptionRate: .12 * reward.PercentDenominator,
   125  			MinConsumptionRate: .10 * reward.PercentDenominator,
   126  			MintingPeriod:      365 * 24 * time.Hour,
   127  			SupplyCap:          720 * units.MegaAvax,
   128  		}),
   129  	)
   130  	require.NoError(err)
   131  
   132  	m := NewManager(
   133  		logging.NoLog{},
   134  		config.Config{
   135  			Validators: vdrs,
   136  		},
   137  		s,
   138  		metrics,
   139  		new(mockable.Clock),
   140  	)
   141  
   142  	var (
   143  		nodeIDs       []ids.NodeID
   144  		currentHeight uint64
   145  	)
   146  	for i := 0; i < 50; i++ {
   147  		currentHeight++
   148  		nodeID, err := addPrimaryValidator(s, genesisTime, genesisEndTime, currentHeight)
   149  		require.NoError(err)
   150  		nodeIDs = append(nodeIDs, nodeID)
   151  	}
   152  	subnetID := ids.GenerateTestID()
   153  	for _, nodeID := range nodeIDs {
   154  		currentHeight++
   155  		require.NoError(addSubnetValidator(s, subnetID, genesisTime, genesisEndTime, nodeID, currentHeight))
   156  	}
   157  	for i := 0; i < 9900; i++ {
   158  		currentHeight++
   159  		require.NoError(addSubnetDelegator(s, subnetID, genesisTime, genesisEndTime, nodeIDs, currentHeight))
   160  	}
   161  
   162  	ctx := context.Background()
   163  	height, err := m.GetCurrentHeight(ctx)
   164  	require.NoError(err)
   165  	require.Equal(currentHeight, height)
   166  
   167  	b.ResetTimer()
   168  
   169  	for i := 0; i < b.N; i++ {
   170  		_, err := m.GetValidatorSet(ctx, 0, subnetID)
   171  		require.NoError(err)
   172  	}
   173  
   174  	b.StopTimer()
   175  }
   176  
   177  func addPrimaryValidator(
   178  	s state.State,
   179  	startTime time.Time,
   180  	endTime time.Time,
   181  	height uint64,
   182  ) (ids.NodeID, error) {
   183  	sk, err := bls.NewSecretKey()
   184  	if err != nil {
   185  		return ids.EmptyNodeID, err
   186  	}
   187  
   188  	nodeID := ids.GenerateTestNodeID()
   189  	s.PutCurrentValidator(&state.Staker{
   190  		TxID:            ids.GenerateTestID(),
   191  		NodeID:          nodeID,
   192  		PublicKey:       bls.PublicFromSecretKey(sk),
   193  		SubnetID:        constants.PrimaryNetworkID,
   194  		Weight:          2 * units.MegaAvax,
   195  		StartTime:       startTime,
   196  		EndTime:         endTime,
   197  		PotentialReward: 0,
   198  		NextTime:        endTime,
   199  		Priority:        txs.PrimaryNetworkValidatorCurrentPriority,
   200  	})
   201  
   202  	blk, err := block.NewBanffStandardBlock(startTime, ids.GenerateTestID(), height, nil)
   203  	if err != nil {
   204  		return ids.EmptyNodeID, err
   205  	}
   206  
   207  	s.AddStatelessBlock(blk)
   208  	s.SetHeight(height)
   209  	return nodeID, s.Commit()
   210  }
   211  
   212  func addSubnetValidator(
   213  	s state.State,
   214  	subnetID ids.ID,
   215  	startTime time.Time,
   216  	endTime time.Time,
   217  	nodeID ids.NodeID,
   218  	height uint64,
   219  ) error {
   220  	s.PutCurrentValidator(&state.Staker{
   221  		TxID:            ids.GenerateTestID(),
   222  		NodeID:          nodeID,
   223  		SubnetID:        subnetID,
   224  		Weight:          1 * units.Avax,
   225  		StartTime:       startTime,
   226  		EndTime:         endTime,
   227  		PotentialReward: 0,
   228  		NextTime:        endTime,
   229  		Priority:        txs.SubnetPermissionlessValidatorCurrentPriority,
   230  	})
   231  
   232  	blk, err := block.NewBanffStandardBlock(startTime, ids.GenerateTestID(), height, nil)
   233  	if err != nil {
   234  		return err
   235  	}
   236  
   237  	s.AddStatelessBlock(blk)
   238  	s.SetHeight(height)
   239  	return s.Commit()
   240  }
   241  
   242  func addSubnetDelegator(
   243  	s state.State,
   244  	subnetID ids.ID,
   245  	startTime time.Time,
   246  	endTime time.Time,
   247  	nodeIDs []ids.NodeID,
   248  	height uint64,
   249  ) error {
   250  	i := rand.Intn(len(nodeIDs)) //#nosec G404
   251  	nodeID := nodeIDs[i]
   252  	s.PutCurrentDelegator(&state.Staker{
   253  		TxID:            ids.GenerateTestID(),
   254  		NodeID:          nodeID,
   255  		SubnetID:        subnetID,
   256  		Weight:          1 * units.Avax,
   257  		StartTime:       startTime,
   258  		EndTime:         endTime,
   259  		PotentialReward: 0,
   260  		NextTime:        endTime,
   261  		Priority:        txs.SubnetPermissionlessDelegatorCurrentPriority,
   262  	})
   263  
   264  	blk, err := block.NewBanffStandardBlock(startTime, ids.GenerateTestID(), height, nil)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	s.AddStatelessBlock(blk)
   270  	s.SetLastAccepted(blk.ID())
   271  	s.SetHeight(height)
   272  	return s.Commit()
   273  }