Skip to content

Commit bb043da

Browse files
authored
Throughput Testing Script (#1796)
Signed-off-by: Sam Liokumovich <[email protected]>
1 parent ab79f15 commit bb043da

File tree

18 files changed

+2802
-91
lines changed

18 files changed

+2802
-91
lines changed

cli/cluster_info.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package cli
5+
6+
import (
7+
"fmt"
8+
"os"
9+
"strings"
10+
11+
"github.com/ava-labs/avalanchego/ids"
12+
"gopkg.in/yaml.v2"
13+
14+
"github.com/ava-labs/hypersdk/utils"
15+
)
16+
17+
type ClusterInfo struct {
18+
ChainID string `yaml:"CHAIN_ID"` // ids.ID requires "first and last characters to be quotes"
19+
SubnetID string `yaml:"SUBNET_ID"`
20+
APIs []struct {
21+
CloudID string `yaml:"CLOUD_ID"`
22+
IP string `yaml:"IP"`
23+
Region string `yaml:"REGION"`
24+
} `yaml:"API"`
25+
Validators []struct {
26+
CloudID string `yaml:"CLOUD_ID"`
27+
IP string `yaml:"IP"`
28+
Region string `yaml:"REGION"`
29+
NodeID string `yaml:"NODE_ID"`
30+
} `yaml:"VALIDATOR"`
31+
}
32+
33+
func ReadClusterInfoFile(cliPath string) (ids.ID, map[string]string, error) {
34+
// Load yaml file
35+
yamlFile, err := os.ReadFile(cliPath)
36+
if err != nil {
37+
return ids.Empty, nil, err
38+
}
39+
var yamlContents ClusterInfo
40+
if err := yaml.Unmarshal(yamlFile, &yamlContents); err != nil {
41+
return ids.Empty, nil, fmt.Errorf("%w: unable to unmarshal YAML", err)
42+
}
43+
chainID, err := ids.FromString(yamlContents.ChainID)
44+
if err != nil {
45+
return ids.Empty, nil, err
46+
}
47+
48+
// Load nodes
49+
nodes := make(map[string]string)
50+
for i, api := range yamlContents.APIs {
51+
name := fmt.Sprintf("%s-%d (%s)", "API", i, api.Region)
52+
uri := fmt.Sprintf("http://%s:9650/ext/bc/%s", api.IP, chainID)
53+
nodes[name] = uri
54+
}
55+
for i, validator := range yamlContents.Validators {
56+
name := fmt.Sprintf("%s-%d (%s)", "Validator", i, validator.Region)
57+
uri := fmt.Sprintf("http://%s:9650/ext/bc/%s", validator.IP, chainID)
58+
nodes[name] = uri
59+
}
60+
return chainID, nodes, nil
61+
}
62+
63+
func (h *Handler) ImportClusterInfo(cliPath string) error {
64+
oldChains, err := h.DeleteChains()
65+
if err != nil {
66+
return err
67+
}
68+
if len(oldChains) > 0 {
69+
utils.Outf("{{yellow}}deleted old chains:{{/}} %+v\n", oldChains)
70+
}
71+
72+
// Load yaml file
73+
chainID, nodes, err := ReadClusterInfoFile(cliPath)
74+
if err != nil {
75+
return err
76+
}
77+
for name, uri := range nodes {
78+
if err := h.StoreChain(chainID, name); err != nil {
79+
return err
80+
}
81+
utils.Outf(
82+
"{{yellow}}[%s] stored chainID:{{/}} %s {{yellow}}uri:{{/}} %s\n",
83+
name,
84+
chainID,
85+
uri,
86+
)
87+
}
88+
return h.StoreDefaultChain(chainID)
89+
}
90+
91+
func OnlyAPIs(uris map[string]string) []string {
92+
apis := make([]string, 0, len(uris))
93+
for k := range uris {
94+
if !strings.Contains(strings.ToLower(k), "api") {
95+
continue
96+
}
97+
98+
apis = append(apis, uris[k])
99+
}
100+
return apis
101+
}

cli/spam.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ import (
88

99
"github.com/ava-labs/hypersdk/auth"
1010
"github.com/ava-labs/hypersdk/cli/prompt"
11+
"github.com/ava-labs/hypersdk/codec"
1112
"github.com/ava-labs/hypersdk/consts"
13+
"github.com/ava-labs/hypersdk/crypto/ed25519"
1214
"github.com/ava-labs/hypersdk/throughput"
1315
)
1416

15-
// BuildSpammer prompts the user for the spammer parameters. If [defaults], the default values are used once the
17+
// BuildSpammer prompts the user for the spammer parameters.
18+
// If [spamKey] is provided, the user is not prompted for the root key.
19+
// If [defaults], the default values are used once the
1620
// chain and root key are selected. Otherwise, the user is prompted for all parameters.
17-
func (h *Handler) BuildSpammer(sh throughput.SpamHelper, defaults bool) (*throughput.Spammer, error) {
21+
func (h *Handler) BuildSpammer(sh throughput.SpamHelper, spamKey string, defaults bool) (*throughput.Spammer, error) {
1822
// Select chain
1923
chains, err := h.GetChains()
2024
if err != nil {
@@ -25,20 +29,35 @@ func (h *Handler) BuildSpammer(sh throughput.SpamHelper, defaults bool) (*throug
2529
return nil, err
2630
}
2731

28-
// Select root key
29-
keys, err := h.GetKeys()
30-
if err != nil {
31-
return nil, err
32-
}
3332
if err := sh.CreateClient(uris[0]); err != nil {
3433
return nil, err
3534
}
3635

37-
keyIndex, err := prompt.Choice("select root key", len(keys))
38-
if err != nil {
39-
return nil, err
36+
var key *auth.PrivateKey
37+
38+
if len(spamKey) == 0 {
39+
// Select root key
40+
keys, err := h.GetKeys()
41+
if err != nil {
42+
return nil, err
43+
}
44+
keyIndex, err := prompt.Choice("select root key", len(keys))
45+
if err != nil {
46+
return nil, err
47+
}
48+
key = keys[keyIndex]
49+
} else {
50+
bytes, err := codec.LoadHex(spamKey, ed25519.PrivateKeyLen)
51+
if err != nil {
52+
return nil, err
53+
}
54+
privateKey := ed25519.PrivateKey(bytes)
55+
key = &auth.PrivateKey{
56+
Address: auth.NewED25519Address(privateKey.PublicKey()),
57+
Bytes: bytes,
58+
}
4059
}
41-
key := keys[keyIndex]
60+
4261
// No longer using db, so we close
4362
if err := h.CloseDatabase(); err != nil {
4463
return nil, err
@@ -50,7 +69,7 @@ func (h *Handler) BuildSpammer(sh throughput.SpamHelper, defaults bool) (*throug
5069
}
5170

5271
if defaults {
53-
sc := throughput.NewDefaultConfig(uris, authFactory)
72+
sc := throughput.NewFastConfig(uris, authFactory)
5473
return throughput.NewSpammer(sc, sh)
5574
}
5675
// Collect parameters
@@ -102,8 +121,8 @@ func (h *Handler) BuildSpammer(sh throughput.SpamHelper, defaults bool) (*throug
102121
return throughput.NewSpammer(sc, sh)
103122
}
104123

105-
func (h *Handler) Spam(ctx context.Context, sh throughput.SpamHelper, defaults bool) error {
106-
spammer, err := h.BuildSpammer(sh, defaults)
124+
func (h *Handler) Spam(ctx context.Context, sh throughput.SpamHelper, spamKey string, defaults bool) error {
125+
spammer, err := h.BuildSpammer(sh, spamKey, defaults)
107126
if err != nil {
108127
return err
109128
}

examples/morpheusvm/cmd/morpheus-cli/cmd/chain.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ var setChainCmd = &cobra.Command{
2828
},
2929
}
3030

31+
var importAvalancheCliChainCmd = &cobra.Command{
32+
Use: "import-cli [path]",
33+
PreRunE: func(cmd *cobra.Command, args []string) error {
34+
if len(args) != 1 {
35+
return ErrInvalidArgs
36+
}
37+
return nil
38+
},
39+
RunE: func(_ *cobra.Command, args []string) error {
40+
return handler.Root().ImportClusterInfo(args[0])
41+
},
42+
}
43+
3144
var chainInfoCmd = &cobra.Command{
3245
Use: "info",
3346
RunE: func(_ *cobra.Command, _ []string) error {

examples/morpheusvm/cmd/morpheus-cli/cmd/genesis.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ var genGenesisCmd = &cobra.Command{
6464
if minBlockGap >= 0 {
6565
genesis.Rules.MinBlockGap = minBlockGap
6666
}
67+
if validityWindow >= 0 {
68+
genesis.Rules.ValidityWindow = validityWindow
69+
}
6770

6871
b, err := json.Marshal(genesis)
6972
if err != nil {

examples/morpheusvm/cmd/morpheus-cli/cmd/root.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ var (
2828
maxBlockUnits []string
2929
windowTargetUnits []string
3030
minBlockGap int64
31+
validityWindow int64
3132
hideTxs bool
3233
checkAllChains bool
3334
spamDefaults bool
35+
spamKey string
36+
clusterInfo string
3437
prometheusBaseURI string
3538
prometheusOpenBrowser bool
3639
prometheusFile string
3740
prometheusData string
3841
startPrometheus bool
39-
40-
rootCmd = &cobra.Command{
42+
rootCmd = &cobra.Command{
4143
Use: "morpheus-cli",
4244
Short: "MorpheusVM CLI",
4345
SuggestFor: []string{"morpheus-cli", "morpheuscli"},
@@ -106,6 +108,12 @@ func init() {
106108
-1,
107109
"minimum block gap (ms)",
108110
)
111+
genGenesisCmd.PersistentFlags().Int64Var(
112+
&validityWindow,
113+
"validity-window",
114+
-1,
115+
"validity window (ms)",
116+
)
109117
genesisCmd.AddCommand(
110118
genGenesisCmd,
111119
)
@@ -135,6 +143,7 @@ func init() {
135143
importChainCmd,
136144
setChainCmd,
137145
chainInfoCmd,
146+
importAvalancheCliChainCmd,
138147
watchChainCmd,
139148
)
140149

@@ -150,6 +159,20 @@ func init() {
150159
"use default spam parameters",
151160
)
152161

162+
runSpamCmd.PersistentFlags().StringVar(
163+
&clusterInfo,
164+
"cluster-info",
165+
"",
166+
"output from avalanche-cli with cluster info",
167+
)
168+
169+
runSpamCmd.PersistentFlags().StringVar(
170+
&spamKey,
171+
"key",
172+
"",
173+
"private key used to distribute funds",
174+
)
175+
153176
// spam
154177
spamCmd.AddCommand(
155178
runSpamCmd,

examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@ package cmd
55

66
import (
77
"context"
8+
"errors"
89

910
"github.com/spf13/cobra"
1011

12+
"github.com/ava-labs/hypersdk/auth"
13+
"github.com/ava-labs/hypersdk/cli"
14+
"github.com/ava-labs/hypersdk/codec"
15+
"github.com/ava-labs/hypersdk/crypto/ed25519"
1116
"github.com/ava-labs/hypersdk/examples/morpheusvm/throughput"
1217
"github.com/ava-labs/hypersdk/examples/morpheusvm/vm"
18+
"github.com/ava-labs/hypersdk/utils"
19+
20+
hthroughput "github.com/ava-labs/hypersdk/throughput"
1321
)
1422

1523
var spamCmd = &cobra.Command{
@@ -29,6 +37,40 @@ var runSpamCmd = &cobra.Command{
2937
},
3038
RunE: func(_ *cobra.Command, args []string) error {
3139
ctx := context.Background()
32-
return handler.Root().Spam(ctx, &throughput.SpamHelper{KeyType: args[0]}, spamDefaults)
40+
41+
if len(clusterInfo) > 0 {
42+
_, urisFromFile, err := cli.ReadClusterInfoFile(clusterInfo)
43+
if err != nil {
44+
utils.Outf("{{red}} failed to read cluster info: %s \n", err)
45+
return err
46+
}
47+
uris := cli.OnlyAPIs(urisFromFile)
48+
49+
if len(uris) == 0 || len(spamKey) == 0 {
50+
return errors.New("uris and keyHex must be non-empty")
51+
}
52+
bytes, err := codec.LoadHex(spamKey, ed25519.PrivateKeyLen)
53+
if err != nil {
54+
return err
55+
}
56+
authFactory := auth.NewED25519Factory(ed25519.PrivateKey(bytes))
57+
58+
spamConfig, err := hthroughput.NewLongRunningConfig(uris, authFactory)
59+
if err != nil {
60+
return err
61+
}
62+
63+
spamHelper := &throughput.SpamHelper{KeyType: args[0]}
64+
if err := spamHelper.CreateClient(uris[0]); err != nil {
65+
return err
66+
}
67+
spammer, err := hthroughput.NewSpammer(spamConfig, spamHelper)
68+
if err != nil {
69+
return err
70+
}
71+
return spammer.Spam(ctx, spamHelper, true, "AVAX")
72+
}
73+
74+
return handler.Root().Spam(ctx, &throughput.SpamHelper{KeyType: args[0]}, spamKey, spamDefaults)
3375
},
3476
}

0 commit comments

Comments
 (0)