|
| 1 | +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. |
| 2 | +// See the file LICENSE for licensing terms. |
| 3 | + |
| 4 | +package vm |
| 5 | + |
| 6 | +import ( |
| 7 | + "flag" |
| 8 | + "fmt" |
| 9 | + "path/filepath" |
| 10 | + "testing" |
| 11 | + "time" |
| 12 | + |
| 13 | + "github.com/prometheus/client_golang/prometheus" |
| 14 | + "github.com/stretchr/testify/require" |
| 15 | + "go.uber.org/zap" |
| 16 | + |
| 17 | + "github.com/ava-labs/avalanchego/api/metrics" |
| 18 | + "github.com/ava-labs/avalanchego/chains" |
| 19 | + "github.com/ava-labs/avalanchego/database/leveldb" |
| 20 | + "github.com/ava-labs/avalanchego/genesis" |
| 21 | + "github.com/ava-labs/avalanchego/ids" |
| 22 | + "github.com/ava-labs/avalanchego/snow/uptime" |
| 23 | + "github.com/ava-labs/avalanchego/snow/validators" |
| 24 | + "github.com/ava-labs/avalanchego/tests" |
| 25 | + "github.com/ava-labs/avalanchego/tests/reexecute" |
| 26 | + "github.com/ava-labs/avalanchego/upgrade/upgradetest" |
| 27 | + "github.com/ava-labs/avalanchego/utils/constants" |
| 28 | + "github.com/ava-labs/avalanchego/vms/platformvm" |
| 29 | + "github.com/ava-labs/avalanchego/vms/platformvm/config" |
| 30 | +) |
| 31 | + |
| 32 | +var mainnetPChainID = ids.FromStringOrPanic("11111111111111111111111111111111LpoYY") |
| 33 | + |
| 34 | +var ( |
| 35 | + blockDirArg string |
| 36 | + currentStateDirArg string |
| 37 | + startBlockArg uint64 |
| 38 | + endBlockArg uint64 |
| 39 | + chanSizeArg int |
| 40 | + executionTimeout time.Duration |
| 41 | + |
| 42 | + metricsServerEnabledArg bool |
| 43 | + metricsServerPortArg uint64 |
| 44 | + |
| 45 | + runnerNameArg string |
| 46 | +) |
| 47 | + |
| 48 | +func TestMain(m *testing.M) { |
| 49 | + flag.StringVar(&blockDirArg, "block-dir", blockDirArg, "Block DB directory to read from during re-execution.") |
| 50 | + flag.StringVar(¤tStateDirArg, "current-state-dir", currentStateDirArg, "Current state directory including VM DB and Chain Data Directory for re-execution.") |
| 51 | + flag.Uint64Var(&startBlockArg, "start-block", 101, "Start block to begin execution (exclusive).") |
| 52 | + flag.Uint64Var(&endBlockArg, "end-block", 200, "End block to end execution (inclusive).") |
| 53 | + flag.IntVar(&chanSizeArg, "chan-size", 100, "Size of the channel to use for block processing.") |
| 54 | + flag.DurationVar(&executionTimeout, "execution-timeout", 0, "Benchmark execution timeout. After this timeout has elapsed, terminate the benchmark without error. If 0, no timeout is applied.") |
| 55 | + |
| 56 | + flag.BoolVar(&metricsServerEnabledArg, "metrics-server-enabled", false, "Whether to enable the metrics server.") |
| 57 | + flag.Uint64Var(&metricsServerPortArg, "metrics-server-port", 0, "The port the metrics server will listen to.") |
| 58 | + |
| 59 | + flag.StringVar(&runnerNameArg, "runner", "dev", "Name of the runner executing this test. Added as a metric label and to the sub-benchmark's name to differentiate results on the runner key.") |
| 60 | + |
| 61 | + m.Run() |
| 62 | +} |
| 63 | + |
| 64 | +func BenchmarkReexecuteRange(b *testing.B) { |
| 65 | + require.Equalf(b, 1, b.N, "BenchmarkReexecuteRange expects to run a single iteration because it overwrites the input current-state, but found (b.N=%d)", b.N) |
| 66 | + b.Run(fmt.Sprintf("[%d,%d]-Runner-%s", startBlockArg, endBlockArg, runnerNameArg), func(b *testing.B) { |
| 67 | + benchmarkReexecuteRange( |
| 68 | + b, |
| 69 | + blockDirArg, |
| 70 | + currentStateDirArg, |
| 71 | + startBlockArg, |
| 72 | + endBlockArg, |
| 73 | + chanSizeArg, |
| 74 | + metricsServerEnabledArg, |
| 75 | + metricsServerPortArg, |
| 76 | + ) |
| 77 | + }) |
| 78 | +} |
| 79 | + |
| 80 | +func benchmarkReexecuteRange( |
| 81 | + b *testing.B, |
| 82 | + blockDir string, |
| 83 | + currentStateDir string, |
| 84 | + startBlock uint64, |
| 85 | + endBlock uint64, |
| 86 | + chanSize int, |
| 87 | + metricsServerEnabled bool, |
| 88 | + metricsPort uint64, |
| 89 | +) { |
| 90 | + r := require.New(b) |
| 91 | + ctx := b.Context() |
| 92 | + |
| 93 | + // Create the prefix gatherer passed to the VM and register it with the top-level, |
| 94 | + // labeled gatherer. |
| 95 | + prefixGatherer := metrics.NewPrefixGatherer() |
| 96 | + |
| 97 | + vmMultiGatherer := metrics.NewPrefixGatherer() |
| 98 | + // XXX: find out what the correct P-chain prefix is |
| 99 | + r.NoError(prefixGatherer.Register("avalanche_p", vmMultiGatherer)) |
| 100 | + |
| 101 | + log := tests.NewDefaultLogger("p-chain-reexecution") |
| 102 | + if metricsServerEnabled { |
| 103 | + reexecute.StartServer(b, log, prefixGatherer, metricsPort) |
| 104 | + } |
| 105 | + |
| 106 | + var ( |
| 107 | + vmDBDir = filepath.Join(currentStateDir, "db") |
| 108 | + chainDataDir = filepath.Join(currentStateDir, "chain-data-dir") |
| 109 | + ) |
| 110 | + |
| 111 | + log.Info("re-executing block range with params", |
| 112 | + zap.String("block-dir", blockDir), |
| 113 | + zap.String("vm-db-dir", vmDBDir), |
| 114 | + zap.String("chain-data-dir", chainDataDir), |
| 115 | + zap.Uint64("start-block", startBlock), |
| 116 | + zap.Uint64("end-block", endBlock), |
| 117 | + zap.Int("chan-size", chanSize), |
| 118 | + ) |
| 119 | + |
| 120 | + dbLogger := tests.NewDefaultLogger("db") |
| 121 | + |
| 122 | + db, err := leveldb.New(vmDBDir, nil, dbLogger, prometheus.NewRegistry()) |
| 123 | + r.NoError(err) |
| 124 | + defer func() { |
| 125 | + log.Info("shutting down DB") |
| 126 | + r.NoError(db.Close()) |
| 127 | + }() |
| 128 | + |
| 129 | + genesisConfig := genesis.GetConfig(constants.MainnetID) |
| 130 | + genesisBytes, _, err := genesis.FromConfig(genesisConfig) |
| 131 | + r.NoError(err) |
| 132 | + |
| 133 | + vmParams := reexecute.VMParams{ |
| 134 | + GenesisBytes: genesisBytes, |
| 135 | + SubnetID: constants.PrimaryNetworkID, |
| 136 | + ChainID: mainnetPChainID, |
| 137 | + } |
| 138 | + |
| 139 | + vm, err := reexecute.NewMainnetVM( |
| 140 | + ctx, |
| 141 | + &platformvm.Factory{ |
| 142 | + Internal: mainnetPChainInternalConfig(), |
| 143 | + }, |
| 144 | + db, |
| 145 | + chainDataDir, |
| 146 | + vmMultiGatherer, |
| 147 | + vmParams, |
| 148 | + ) |
| 149 | + r.NoError(err) |
| 150 | + defer func() { |
| 151 | + log.Info("shutting down VM") |
| 152 | + r.NoError(vm.Shutdown(ctx)) |
| 153 | + }() |
| 154 | + |
| 155 | + executor := reexecute.NewVMExecutor( |
| 156 | + b, |
| 157 | + vm, |
| 158 | + blockDir, |
| 159 | + startBlock, |
| 160 | + endBlock, |
| 161 | + chanSize, |
| 162 | + executionTimeout, |
| 163 | + prefixGatherer, |
| 164 | + ) |
| 165 | + |
| 166 | + r.NoError(executor.Run(ctx)) |
| 167 | +} |
| 168 | + |
| 169 | +func mainnetPChainInternalConfig() config.Internal { |
| 170 | + return config.Internal{ |
| 171 | + Chains: chains.TestManager, |
| 172 | + UptimeLockedCalculator: uptime.NewLockedCalculator(), |
| 173 | + SybilProtectionEnabled: true, |
| 174 | + Validators: validators.NewManager(), |
| 175 | + DynamicFeeConfig: genesis.MainnetParams.DynamicFeeConfig, |
| 176 | + ValidatorFeeConfig: genesis.MainnetParams.ValidatorFeeConfig, |
| 177 | + MinValidatorStake: genesis.MainnetParams.MinValidatorStake, |
| 178 | + MaxValidatorStake: genesis.MainnetParams.MaxValidatorStake, |
| 179 | + MinDelegatorStake: genesis.MainnetParams.MinDelegatorStake, |
| 180 | + MinStakeDuration: genesis.MainnetParams.MinStakeDuration, |
| 181 | + MaxStakeDuration: genesis.MainnetParams.MaxStakeDuration, |
| 182 | + RewardConfig: genesis.MainnetParams.RewardConfig, |
| 183 | + UpgradeConfig: upgradetest.GetConfig(upgradetest.Latest), |
| 184 | + } |
| 185 | +} |
0 commit comments