diff --git a/.gitignore b/.gitignore index 51765b7253..8e0300f5f3 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ bin/ build/ cmd/simulator/.simulator/* +cmd/simulator/simulator # goreleaser dist/ diff --git a/cmd/simulator/.gitignore b/cmd/simulator/.gitignore deleted file mode 100644 index c4408cd691..0000000000 --- a/cmd/simulator/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -simulator - -*.log -*~ -.DS_Store - -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -*.profile - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# ignore GoLand metafiles directory -.idea/ - -*logs/ - -.vscode* - -.coverage - -bin/ -build/ - -# goreleaser -dist/ diff --git a/cmd/simulator/README.md b/cmd/simulator/README.md index 64a619dca0..73c4a75ab9 100644 --- a/cmd/simulator/README.md +++ b/cmd/simulator/README.md @@ -1,6 +1,6 @@ # Load Simulator -When building developing your own blockchain using `subnet-evm`, you may want to analyze how your fee parameterization behaves and/or how many resources your VM uses under different load patterns. For this reason, we developed `cmd/simulator`. `cmd/simulator` lets you drive arbitrary load across any number of [endpoints] with a user-specified `keys` directory (insecure) `timeout`, `concurrency`, `base-fee`, and `priority-fee`. +When building developing your own blockchain using `subnet-evm`, you may want to analyze how your fee parameterization behaves and/or how many resources your VM uses under different load patterns. For this reason, we developed `cmd/simulator`. `cmd/simulator` lets you drive arbitrary load across any number of [endpoints] with a user-specified `keys` directory (insecure) `timeout`, `workers`, `max-fee-cap`, and `max-tip-cap`. ## Building the Load Simulator @@ -13,19 +13,19 @@ cd $GOPATH/src/github.com/ava-labs/subnet-evm/cmd/simulator Build the simulator: ```bash -go build -o ./simulator *.go +go build -o ./simulator main/*.go ``` To confirm that you built successfully, run the simulator and print the version: ```bash -./simulator -v +./simulator --version ``` This should give the following output: ``` -v0.0.1 +v0.1.0 ``` To run the load simulator, you must first start an EVM based network. The load simulator works on both the C-Chain and Subnet-EVM, so we will start a single node network and run the load simulator on the C-Chain. @@ -45,45 +45,18 @@ The staking-enabled flag is only for local testing. Disabling staking serves two 2. Automatically opts in to validate every Subnet ::: -Once you have AvalancheGo running locally, it will be running an HTTP Server on the default port `9650`. This means that the RPC Endpoint for the C-Chain will be http://127.0.0.1:9650/ext/bc/C/rpc. +Once you have AvalancheGo running locally, it will be running an HTTP Server on the default port `9650`. This means that the RPC Endpoint for the C-Chain will be http://127.0.0.1:9650/ext/bc/C/rpc and ws://127.0.0.1:9650/ext/bc/C/ws for WebSocket connections. Now, we can run the simulator command to simulate some load on the local C-Chain for 30s: ```bash -RPC_ENDPOINTS=http://127.0.0.1:9650/ext/bc/C/rpc -./simulator --rpc-endpoints=$RPC_ENDPOINTS --keys=./.simulator/keys --timeout=30s --concurrency=10 --base-fee=300 --priority-fee=100 +./simulator --timeout=1m --concurrency=1 --max-fee-cap=300 --max-tip-cap=10 --txs-per-worker=50 ``` ## Command Line Flags -### `rpc-endpoints` (string) +To see all of the command line flag options, run -`rpc-endpoints` is a comma separated list of RPC endpoints to hit during the load test. - -### `keys` (string) - -`keys` specifies the directory to find the private keys to use throughout the test. The directory should contain files with the hex address as the name and the corresponding private key as the only content. - -If the test needs to generate more keys (to meet the number of workers specified by `concurrency`), it will save them in this directory to ensure that the private keys holding funds at the end of the test are preserved. - -:::warning -The `keys` directory is not a secure form of storage for private keys. This should only be used in local network and short lived network testing where losing or compromising the keys is not an issue. -::: - -Note: if none of the keys in this directory have any funds, then the simulator will log an address that it expects to receive funds in order to fund the load test and wait for those funds to arrive. - -### `timeout` (duration) - -`timeout` specifies the duration to simulate load on the network for this test. - -### `concurrency` (int) - -`concurrency` specifies the number of concurrent workers that should send transactions to the network throughout the test. Each worker in the load test is a pairing of a private key and an RPC Endpoint. The private key is used to generate a stream of transactions, which are issued to the RPC Endpoint. - -### `base-fee` (int) - -`base-fee` specifies the base fee (denominated in GWei) to use for every transaction generated during the load test (generates Dynamic Fee Transactions). - -### `priority-fee` (int) - -`priority-fee` specifies the priority fee (denominated in GWei) to use for every transaction generated during the load test (generates Dynamic Fee Transactions). +```bash +./simulator --help +``` diff --git a/cmd/simulator/config/flags.go b/cmd/simulator/config/flags.go new file mode 100644 index 0000000000..b071d3adc9 --- /dev/null +++ b/cmd/simulator/config/flags.go @@ -0,0 +1,117 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package config + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +const Version = "v0.1.0" + +const ( + ConfigFilePathKey = "config-file" + LogLevelKey = "log-level" + EndpointsKey = "endpoints" + MaxFeeCapKey = "max-fee-cap" + MaxTipCapKey = "max-tip-cap" + WorkersKey = "workers" + TxsPerWorkerKey = "txs-per-worker" + KeyDirKey = "key-dir" + VersionKey = "version" + TimeoutKey = "timeout" +) + +var ( + ErrNoEndpoints = errors.New("must specify at least one endpoint") + ErrNoWorkers = errors.New("must specify non-zero number of workers") + ErrNoTxs = errors.New("must specify non-zero number of txs-per-worker") +) + +type Config struct { + Endpoints []string `json:"endpoints"` + MaxFeeCap int64 `json:"max-fee-cap"` + MaxTipCap int64 `json:"max-tip-cap"` + Workers int `json:"workers"` + TxsPerWorker uint64 `json:"txs-per-worker"` + KeyDir string `json:"key-dir"` + Timeout time.Duration `json:"timeout"` +} + +func BuildConfig(v *viper.Viper) (Config, error) { + c := Config{ + Endpoints: v.GetStringSlice(EndpointsKey), + MaxFeeCap: v.GetInt64(MaxFeeCapKey), + MaxTipCap: v.GetInt64(MaxTipCapKey), + Workers: v.GetInt(WorkersKey), + TxsPerWorker: v.GetUint64(TxsPerWorkerKey), + KeyDir: v.GetString(KeyDirKey), + Timeout: v.GetDuration(TimeoutKey), + } + if len(c.Endpoints) == 0 { + return c, ErrNoEndpoints + } + if c.Workers == 0 { + return c, ErrNoWorkers + } + if c.TxsPerWorker == 0 { + return c, ErrNoTxs + } + // Note: it's technically valid for the fee/tip cap to be 0, but cannot + // be less than 0. + if c.MaxFeeCap < 0 { + return c, fmt.Errorf("invalid max fee cap %d < 0", c.MaxFeeCap) + } + if c.MaxTipCap < 0 { + return c, fmt.Errorf("invalid max tip cap %d <= 0", c.MaxTipCap) + } + return c, nil +} + +func BuildViper(fs *pflag.FlagSet, args []string) (*viper.Viper, error) { + if err := fs.Parse(args); err != nil { + return nil, err + } + + v := viper.New() + v.AutomaticEnv() + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + v.SetEnvPrefix("evm_simulator") + if err := v.BindPFlags(fs); err != nil { + return nil, err + } + + if v.IsSet(ConfigFilePathKey) { + v.SetConfigFile(v.GetString(ConfigFilePathKey)) + if err := v.ReadInConfig(); err != nil { + return nil, err + } + } + return v, nil +} + +// BuildFlagSet returns a complete set of flags for simulator +func BuildFlagSet() *pflag.FlagSet { + fs := pflag.NewFlagSet("simulator", pflag.ContinueOnError) + addSimulatorFlags(fs) + return fs +} + +func addSimulatorFlags(fs *pflag.FlagSet) { + fs.Bool(VersionKey, false, "Print the version and exit") + fs.String(ConfigFilePathKey, "", "Specify the config path to use to load a YAML config for the simulator") + fs.StringSlice(EndpointsKey, []string{"ws://127.0.0.1:9650/ext/bc/C/ws"}, "Specify a comma separated list of RPC Websocket Endpoints (minimum of 1 endpoint)") + fs.Int64(MaxFeeCapKey, 50, "Specify the maximum fee cap to use for transactions denominated in GWei (must be > 0)") + fs.Int64(MaxTipCapKey, 1, "Specify the max tip cap for transactions denominated in GWei (must be >= 0)") + fs.Uint64(TxsPerWorkerKey, 100, "Specify the number of transactions to create per worker (must be > 0)") + fs.Int(WorkersKey, 1, "Specify the number of workers to create for the simulator (must be > 0)") + fs.String(KeyDirKey, ".simulator/keys", "Specify the directory to save private keys in (INSECURE: only use for testing)") + fs.Duration(TimeoutKey, 5*time.Minute, "Specify the timeout for the simulator to complete (0 indicates no timeout)") + fs.String(LogLevelKey, "info", "Specify the log level to use in the simulator") +} diff --git a/cmd/simulator/go.mod b/cmd/simulator/go.mod deleted file mode 100644 index 824812ec56..0000000000 --- a/cmd/simulator/go.mod +++ /dev/null @@ -1,45 +0,0 @@ -module github.com/ava-labs/subnet-evm/cmd/simulator - -go 1.19 - -require ( - github.com/ethereum/go-ethereum v1.10.26 - github.com/spf13/cobra v1.5.0 - golang.org/x/sync v0.1.0 - sigs.k8s.io/yaml v1.3.0 -) - -require ( - github.com/VictoriaMetrics/fastcache v1.10.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/deckarep/golang-set v1.8.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/prometheus/tsdb v0.10.0 // indirect - github.com/rjeczalik/notify v0.9.2 // indirect - github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect - github.com/stretchr/testify v1.8.1 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect - github.com/tyler-smith/go-bip39 v1.0.2 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect -) diff --git a/cmd/simulator/go.sum b/cmd/simulator/go.sum deleted file mode 100644 index 4a262340f5..0000000000 --- a/cmd/simulator/go.sum +++ /dev/null @@ -1,261 +0,0 @@ -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= -github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= -github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= -github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= -github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E= -github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= -github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8= -github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/cmd/simulator/load/funder.go b/cmd/simulator/load/funder.go new file mode 100644 index 0000000000..6e502a7ba7 --- /dev/null +++ b/cmd/simulator/load/funder.go @@ -0,0 +1,122 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + + "github.com/ava-labs/subnet-evm/cmd/simulator/key" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +// DistributeFunds ensures that each address in keys has at least [minFundsPerAddr] by sending funds +// from the key with the highest starting balance. +// This function returns a set of at least [numKeys] keys, each having a minimum balance [minFundsPerAddr]. +func DistributeFunds(ctx context.Context, client ethclient.Client, keys []*key.Key, numKeys int, minFundsPerAddr *big.Int) ([]*key.Key, error) { + if len(keys) < numKeys { + return nil, fmt.Errorf("insufficient number of keys %d < %d", len(keys), numKeys) + } + fundedKeys := make([]*key.Key, 0, numKeys) + // TODO: clean up fund distribution. + needFundsKeys := make([]*key.Key, 0) + needFundsAddrs := make([]common.Address, 0) + + maxFundsKey := keys[0] + maxFundsBalance := common.Big0 + log.Info("Checking balance of each key to distribute funds") + for _, key := range keys { + balance, err := client.BalanceAt(ctx, key.Address, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch balance for addr %s: %w", key.Address, err) + } + + if balance.Cmp(minFundsPerAddr) < 0 { + needFundsKeys = append(needFundsKeys, key) + needFundsAddrs = append(needFundsAddrs, key.Address) + } else { + fundedKeys = append(fundedKeys, key) + } + + if balance.Cmp(maxFundsBalance) > 0 { + maxFundsKey = key + maxFundsBalance = balance + } + } + requiredFunds := new(big.Int).Mul(minFundsPerAddr, big.NewInt(int64(numKeys))) + if maxFundsBalance.Cmp(requiredFunds) < 0 { + return nil, fmt.Errorf("insufficient funds to distribute %d < %d", maxFundsBalance, requiredFunds) + } + log.Info("Found max funded key", "address", maxFundsKey.Address, "balance", maxFundsBalance, "numFundAddrs", len(needFundsAddrs)) + if len(fundedKeys) >= numKeys { + return fundedKeys[:numKeys], nil + } + + // If there are not enough funded keys, cut [needFundsAddrs] to the number of keys that + // must be funded to reach [numKeys] required. + fundKeysCutLen := numKeys - len(fundedKeys) + needFundsKeys = needFundsKeys[:fundKeysCutLen] + needFundsAddrs = needFundsAddrs[:fundKeysCutLen] + + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch chainID: %w", err) + } + gasFeeCap, err := client.EstimateBaseFee(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch estimated base fee: %w", err) + } + gasTipCap, err := client.SuggestGasTipCap(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch suggested gas tip: %w", err) + } + signer := types.LatestSignerForChainID(chainID) + + // Generate a sequence of transactions to distribute the required funds. + log.Info("Generating distribution transactions") + i := 0 + txGenerator := func(key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) { + tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: params.TxGas, + To: &needFundsAddrs[i], + Data: nil, + Value: requiredFunds, + }) + if err != nil { + return nil, err + } + i++ + return tx, nil + } + txs, err := GenerateTxSequence(ctx, txGenerator, client, maxFundsKey.PrivKey, uint64(len(needFundsAddrs))) + if err != nil { + return nil, fmt.Errorf("failed to generate fund distribution sequence from %s of length %d", maxFundsKey.Address, len(needFundsAddrs)) + } + + log.Info("Executing distribution transactions...") + worker := NewWorker(client, maxFundsKey.Address, txs) + if err := worker.Execute(ctx); err != nil { + return nil, err + } + + for _, addr := range needFundsAddrs { + balance, err := client.BalanceAt(ctx, addr, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch balance for addr %s: %w", addr, err) + } + log.Info("Funded address has balance", "balance", balance) + } + fundedKeys = append(fundedKeys, needFundsKeys...) + return fundedKeys, nil +} diff --git a/cmd/simulator/load/loader.go b/cmd/simulator/load/loader.go new file mode 100644 index 0000000000..3fd423a2a5 --- /dev/null +++ b/cmd/simulator/load/loader.go @@ -0,0 +1,119 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + + "github.com/ava-labs/subnet-evm/cmd/simulator/config" + "github.com/ava-labs/subnet-evm/cmd/simulator/key" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ava-labs/subnet-evm/params" + "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" +) + +// CreateLoader creates a WorkerGroup from [config] to perform the specified simulation. +func CreateLoader(ctx context.Context, config config.Config) (*WorkerGroup, error) { + // Construct the arguments for the load simulator + clients := make([]ethclient.Client, 0, len(config.Endpoints)) + for i := 0; i < config.Workers; i++ { + clientURI := config.Endpoints[i%len(config.Endpoints)] + client, err := ethclient.Dial(clientURI) + if err != nil { + return nil, fmt.Errorf("failed to dial client at %s: %w", clientURI, err) + } + clients = append(clients, client) + } + + keys, err := key.LoadAll(ctx, config.KeyDir) + if err != nil { + return nil, err + } + // Ensure there are at least [config.Workers] keys and save any newly generated ones. + if len(keys) < config.Workers { + for i := 0; len(keys) < config.Workers; i++ { + newKey, err := key.Generate() + if err != nil { + return nil, fmt.Errorf("failed to generate %d new key: %w", i, err) + } + if err := newKey.Save(config.KeyDir); err != nil { + return nil, fmt.Errorf("failed to save %d new key: %w", i, err) + } + keys = append(keys, newKey) + } + } + + // Each address needs: params.GWei * MaxFeeCap * params.TxGas * TxsPerWorker total wei + // to fund gas for all of their transactions. + maxFeeCap := new(big.Int).Mul(big.NewInt(params.GWei), big.NewInt(config.MaxFeeCap)) + minFundsPerAddr := new(big.Int).Mul(maxFeeCap, big.NewInt(int64(config.TxsPerWorker*params.TxGas))) + log.Info("Distributing funds", "numTxsPerWorker", config.TxsPerWorker, "minFunds", minFundsPerAddr) + keys, err = DistributeFunds(ctx, clients[0], keys, config.Workers, minFundsPerAddr) + if err != nil { + return nil, err + } + + pks := make([]*ecdsa.PrivateKey, 0, len(keys)) + senders := make([]common.Address, 0, len(keys)) + for _, key := range keys { + pks = append(pks, key.PrivKey) + senders = append(senders, key.Address) + } + + bigGwei := big.NewInt(params.GWei) + gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(config.MaxTipCap)) + gasFeeCap := new(big.Int).Mul(bigGwei, big.NewInt(config.MaxFeeCap)) + client := clients[0] + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch chainID: %w", err) + } + signer := types.LatestSignerForChainID(chainID) + + txGenerator := func(key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) { + addr := ethcrypto.PubkeyToAddress(key.PublicKey) + tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: params.TxGas, + To: &addr, + Data: nil, + Value: common.Big0, + }) + if err != nil { + return nil, err + } + return tx, nil + } + txSequences, err := GenerateTxSequences(ctx, txGenerator, clients[0], pks, config.TxsPerWorker) + if err != nil { + return nil, err + } + + wg := NewWorkerGroup(clients[:config.Workers], senders[:config.Workers], txSequences[:config.Workers]) + return wg, nil +} + +// ExecuteLoader runs the load simulation specified by config. +func ExecuteLoader(ctx context.Context, config config.Config) error { + if config.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, config.Timeout) + defer cancel() + } + + loader, err := CreateLoader(ctx, config) + if err != nil { + return err + } + return loader.Execute(ctx) +} diff --git a/cmd/simulator/load/tx_generator.go b/cmd/simulator/load/tx_generator.go new file mode 100644 index 0000000000..99c6bae76e --- /dev/null +++ b/cmd/simulator/load/tx_generator.go @@ -0,0 +1,46 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +type CreateTx func(key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) + +// GenerateTxSequence fetches the current nonce of key and calls [generator] [numTxs] times sequentially to generate a sequence of transactions. +func GenerateTxSequence(ctx context.Context, generator CreateTx, client ethclient.Client, key *ecdsa.PrivateKey, numTxs uint64) ([]*types.Transaction, error) { + address := ethcrypto.PubkeyToAddress(key.PublicKey) + startingNonce, err := client.NonceAt(ctx, address, nil) + if err != nil { + return nil, fmt.Errorf("failed to fetch nonce for address %s: %w", address, err) + } + txs := make([]*types.Transaction, 0, numTxs) + for i := uint64(0); i < numTxs; i++ { + tx, err := generator(key, startingNonce+i) + if err != nil { + return nil, fmt.Errorf("failed to sign tx at index %d: %w", i, err) + } + txs = append(txs, tx) + } + return txs, nil +} + +func GenerateTxSequences(ctx context.Context, generator CreateTx, client ethclient.Client, keys []*ecdsa.PrivateKey, txsPerKey uint64) ([][]*types.Transaction, error) { + txSequences := make([][]*types.Transaction, len(keys)) + for i, key := range keys { + txs, err := GenerateTxSequence(ctx, generator, client, key, txsPerKey) + if err != nil { + return nil, fmt.Errorf("failed to generate tx sequence at index %d: %w", i, err) + } + txSequences[i] = txs + } + return txSequences, nil +} diff --git a/cmd/simulator/load/worker.go b/cmd/simulator/load/worker.go new file mode 100644 index 0000000000..521298a8a6 --- /dev/null +++ b/cmd/simulator/load/worker.go @@ -0,0 +1,111 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "fmt" + "time" + + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +type Worker struct { + client ethclient.Client + address common.Address + txs []*types.Transaction +} + +// NewWorker creates a new worker that will issue the sequence of transactions from the given address +// +// Assumes that all transactions are from the same address, ordered by nonce, and this worker has exclusive access +// to issuance of transactions from the underlying private key. +func NewWorker(client ethclient.Client, address common.Address, txs []*types.Transaction) *Worker { + return &Worker{ + client: client, + address: address, + txs: txs, + } +} + +func (w *Worker) ExecuteTxsFromAddress(ctx context.Context) error { + log.Info("Executing txs", "numTxs", len(w.txs)) + for i, tx := range w.txs { + start := time.Now() + err := w.client.SendTransaction(ctx, tx) + if err != nil { + return fmt.Errorf("failed to issue tx %d: %w", i, err) + } + log.Info("execute tx", "tx", tx.Hash(), "nonce", tx.Nonce(), "duration", time.Since(start)) + } + return nil +} + +// AwaitTxs awaits for the nonce of the last transaction issued by the worker to be confirmed or +// rejected by the network. +// +// Assumes that a non-zero number of transactions were already generated and that they were issued +// by this worker. +func (w *Worker) AwaitTxs(ctx context.Context) error { + nonce := w.txs[len(w.txs)-1].Nonce() + + newHeads := make(chan *types.Header) + defer close(newHeads) + + sub, err := w.client.SubscribeNewHead(ctx, newHeads) + if err != nil { + log.Debug("failed to subscribe new heads, falling back to polling", "err", err) + } else { + defer sub.Unsubscribe() + } + + for { + select { + case <-newHeads: + case <-time.After(time.Second): + case <-ctx.Done(): + return fmt.Errorf("failed to await nonce: %w", ctx.Err()) + } + + currentNonce, err := w.client.NonceAt(ctx, w.address, nil) + if err != nil { + log.Warn("failed to get nonce", "err", err) + } + if currentNonce >= nonce { + return nil + } else { + log.Info("fetched nonce", "awaiting", nonce, "currentNonce", currentNonce) + } + } +} + +// ConfirmAllTransactions iterates over every transaction of this worker and confirms it +// via eth_getTransactionByHash +func (w *Worker) ConfirmAllTransactions(ctx context.Context) error { + for i, tx := range w.txs { + _, isPending, err := w.client.TransactionByHash(ctx, tx.Hash()) + if err != nil { + return fmt.Errorf("failed to confirm tx at index %d: %s", i, tx.Hash()) + } + if isPending { + return fmt.Errorf("failed to confirm tx at index %d: pending", i) + } + } + log.Info("Confirmed all transactions") + return nil +} + +// Execute issues and confirms all transactions for the worker. +func (w *Worker) Execute(ctx context.Context) error { + if err := w.ExecuteTxsFromAddress(ctx); err != nil { + return err + } + if err := w.AwaitTxs(ctx); err != nil { + return err + } + return w.ConfirmAllTransactions(ctx) +} diff --git a/cmd/simulator/load/workers.go b/cmd/simulator/load/workers.go new file mode 100644 index 0000000000..8dd5cbb878 --- /dev/null +++ b/cmd/simulator/load/workers.go @@ -0,0 +1,106 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "time" + + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "golang.org/x/sync/errgroup" +) + +type WorkerGroup struct { + workers []*Worker +} + +func NewWorkerGroup(clients []ethclient.Client, senders []common.Address, txSequences [][]*types.Transaction) *WorkerGroup { + workers := make([]*Worker, len(clients)) + for i, client := range clients { + workers[i] = NewWorker(client, senders[i], txSequences[i]) + } + + return &WorkerGroup{ + workers: workers, + } +} + +func (wg *WorkerGroup) executeTask(ctx context.Context, f func(ctx context.Context, w *Worker) error) error { + eg := errgroup.Group{} + for _, worker := range wg.workers { + worker := worker + eg.Go(func() error { + return f(ctx, worker) + }) + } + + return eg.Wait() +} + +// IssueTxs concurrently issues transactions from each worker +func (wg *WorkerGroup) IssueTxs(ctx context.Context) error { + return wg.executeTask(ctx, func(ctx context.Context, w *Worker) error { + return w.ExecuteTxsFromAddress(ctx) + }) +} + +// AwaitTxs concurrently waits for each worker to confirm the nonce of its last issued transaction. +func (wg *WorkerGroup) AwaitTxs(ctx context.Context) error { + return wg.executeTask(ctx, func(ctx context.Context, w *Worker) error { + return w.AwaitTxs(ctx) + }) +} + +// ConfirmAllTransactions concurrently waits for each worker to confirm each transaction by checking +// for it via eth_getTransactionByHash +func (wg *WorkerGroup) ConfirmAllTransactions(ctx context.Context) error { + return wg.executeTask(ctx, func(ctx context.Context, w *Worker) error { + return w.ConfirmAllTransactions(ctx) + }) +} + +// Execute performs executes the following steps in order: +// - IssueTxs +// - AwaitTxs +// - ConfirmAllTransactions +func (wg *WorkerGroup) Execute(ctx context.Context) error { + start := time.Now() + defer func() { + log.Info("Completed execution", "totalTime", time.Since(start)) + }() + + executionStart := time.Now() + log.Info("Executing transactions", "startTime", executionStart) + if err := wg.IssueTxs(ctx); err != nil { + return err + } + + awaitStart := time.Now() + log.Info("Awaiting transactions", "startTime", awaitStart, "executionElapsed", awaitStart.Sub(executionStart)) + if err := wg.AwaitTxs(ctx); err != nil { + return err + } + + confirmationStart := time.Now() + log.Info("Confirming transactions", "startTime", confirmationStart, "awaitElapsed", confirmationStart.Sub(awaitStart)) + if err := wg.ConfirmAllTransactions(ctx); err != nil { + return err + } + + endTime := time.Now() + log.Info("Transaction confirmation completed", "confirmationElapsed", endTime.Sub(confirmationStart)) + totalTxs := 0 + for _, worker := range wg.workers { + totalTxs += len(worker.txs) + } + + // We exclude the time for ConfirmAllTransactions because we assume once the nonce is reported, the transaction has been finalized. + // For Subnet-EVM, this assumes that unfinalized data is not exposed via API. + issueAndConfirmDuration := confirmationStart.Sub(executionStart) + log.Info("Simulator completed", "totalTxs", totalTxs, "TPS", float64(totalTxs)/issueAndConfirmDuration.Seconds()) + return nil +} diff --git a/cmd/simulator/main.go b/cmd/simulator/main.go deleted file mode 100644 index 2063cf471c..0000000000 --- a/cmd/simulator/main.go +++ /dev/null @@ -1,106 +0,0 @@ -// (c) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package main - -import ( - "context" - "errors" - "fmt" - "log" - "os" - "os/signal" - "syscall" - "time" - - "github.com/ava-labs/subnet-evm/cmd/simulator/worker" - "github.com/spf13/cobra" -) - -func init() { - cobra.EnablePrefixMatching = true -} - -func main() { - rootCmd := newCommand() - if err := rootCmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "simulator failed %v\n", err) - os.Exit(1) - } -} - -var ( - version = "v0.0.1" - versionFlag bool - timeout time.Duration - keysDir string - - rpcEndpoints []string - - concurrency int - baseFee uint64 - priorityFee uint64 - - defaultLocalNetworkCChainEndpoints = []string{ - "http://127.0.0.1:9650/ext/bc/C/rpc", - "http://127.0.0.1:9652/ext/bc/C/rpc", - "http://127.0.0.1:9654/ext/bc/C/rpc", - "http://127.0.0.1:9656/ext/bc/C/rpc", - "http://127.0.0.1:9658/ext/bc/C/rpc", - } -) - -func newCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "simulator", - Short: "Load simulator for subnet-evm + C-chain", - SuggestFor: []string{"simulators"}, - Run: runFunc, - } - - cmd.PersistentFlags().BoolVarP(&versionFlag, "version", "v", false, "Print the version of the simulator and exit.") - cmd.PersistentFlags().DurationVarP(&timeout, "timeout", "t", time.Minute, "Duration to run simulator") - cmd.PersistentFlags().StringVarP(&keysDir, "keys", "k", ".simulator/keys", "Directory to find key files") - cmd.PersistentFlags().StringSliceVarP(&rpcEndpoints, "rpc-endpoints", "e", defaultLocalNetworkCChainEndpoints, `Specifies a comma separated list of RPC Endpoints to use for the load test. Ex. "http://127.0.0.1:9650/ext/bc/C/rpc,http://127.0.0.1:9652/ext/bc/C/rpc". Defaults to the default RPC Endpoints for the C-Chain on a 5 Node local network.`) - cmd.PersistentFlags().IntVarP(&concurrency, "concurrency", "c", 10, "Number of concurrent workers to use during the load test") - cmd.PersistentFlags().Uint64VarP(&baseFee, "base-fee", "f", 25, "Base fee to use for each transaction issued into the load test") - cmd.PersistentFlags().Uint64VarP(&priorityFee, "priority-fee", "p", 1, "Priority fee to use for each transaction issued into the load test") - - return cmd -} - -func runFunc(cmd *cobra.Command, args []string) { - if versionFlag { - fmt.Printf("%s\n", version) - return - } - // TODO: use geth logger - log.Printf("launching simulator with rpc endpoints %q timeout %v, concurrency %d, base fee %d, priority fee %d", - rpcEndpoints, timeout, concurrency, baseFee, priorityFee) - - cfg := &worker.Config{ - Endpoints: rpcEndpoints, - Concurrency: concurrency, - BaseFee: baseFee, - PriorityFee: priorityFee, - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - errc := make(chan error) - go func() { - errc <- worker.Run(ctx, cfg, keysDir) - }() - - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT) - select { - case sig := <-sigs: - log.Printf("received OS signal %v; canceling context", sig.String()) - cancel() - case err := <-errc: - cancel() - if !errors.Is(err, context.DeadlineExceeded) { - log.Fatalf("worker.Run returned an error %v", err) - } - } -} diff --git a/cmd/simulator/main/main.go b/cmd/simulator/main/main.go new file mode 100644 index 0000000000..141d09b440 --- /dev/null +++ b/cmd/simulator/main/main.go @@ -0,0 +1,56 @@ +// (c) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/ava-labs/subnet-evm/cmd/simulator/config" + "github.com/ava-labs/subnet-evm/cmd/simulator/load" + "github.com/ethereum/go-ethereum/log" + "github.com/spf13/pflag" +) + +func main() { + fs := config.BuildFlagSet() + v, err := config.BuildViper(fs, os.Args[1:]) + if errors.Is(err, pflag.ErrHelp) { + os.Exit(0) + } + + if err != nil { + fmt.Printf("couldn't build viper: %s\n", err) + os.Exit(1) + } + + if err != nil { + fmt.Printf("couldn't configure flags: %s\n", err) + os.Exit(1) + } + + if v.GetBool(config.VersionKey) { + fmt.Printf("%s\n", config.Version) + os.Exit(0) + } + + logLevel, err := log.LvlFromString(v.GetString(config.LogLevelKey)) + if err != nil { + fmt.Printf("couldn't parse log level: %s\n", err) + os.Exit(1) + } + log.Root().SetHandler(log.LvlFilterHandler(logLevel, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + + config, err := config.BuildConfig(v) + if err != nil { + fmt.Printf("%s\n", err) + os.Exit(1) + } + if err := load.ExecuteLoader(context.Background(), config); err != nil { + fmt.Printf("load execution failed: %s\n", err) + os.Exit(1) + } +} diff --git a/cmd/simulator/metrics/metrics.go b/cmd/simulator/metrics/metrics.go deleted file mode 100644 index 4df658164a..0000000000 --- a/cmd/simulator/metrics/metrics.go +++ /dev/null @@ -1,94 +0,0 @@ -// (c) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package metrics - -import ( - "context" - "fmt" - "log" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/params" -) - -const ( - checkInterval = 2 * time.Second -) - -// Monitor periodically prints metrics related to transaction activity on -// a given network. -func Monitor(ctx context.Context, client *ethclient.Client) error { - lastBlockNumber, err := client.BlockNumber(ctx) - if err != nil { - return fmt.Errorf("failed to get block number: %w", err) - } - startTime := uint64(time.Now().Unix()) - currentTime := startTime - totalTxs := 0 - timeTxs := make(map[uint64]int) - totalGas := uint64(0) - timeGas := make(map[uint64]uint64) - for ctx.Err() == nil { - newBlockNumber, err := client.BlockNumber(ctx) - if err != nil { - log.Printf("failed to get block number: %s", err) - time.Sleep(checkInterval) - continue - } - - if newBlockNumber <= lastBlockNumber { - time.Sleep(checkInterval) - continue - } - - var block *types.Block - for i := lastBlockNumber + 1; i <= newBlockNumber; i++ { - for ctx.Err() == nil { - block, err = client.BlockByNumber(ctx, new(big.Int).SetUint64(i)) - if err != nil { - log.Printf("failed to get block at number %d: %s", i, err) - time.Sleep(checkInterval) - continue - } - txs := len(block.Transactions()) - gas := block.GasUsed() - t := block.Time() - - log.Printf("[block created] t: %v index: %d base fee: %d block txs: %d gas used: %d\n", time.Unix(int64(t), 0), i, block.BaseFee().Div(block.BaseFee(), big.NewInt(params.GWei)), txs, gas) - - // Update Tx Count - timeTxs[t] += txs - totalTxs += txs - - // Update Gas Usage - timeGas[t] += gas - totalGas += gas - break - } - } - lastBlockNumber = newBlockNumber - currentTime = block.Time() - - // log 10s - tenStxs := 0 - tenSgas := uint64(0) - for i := uint64(currentTime - 10); i < currentTime; i++ { // ensure only looking at closed times - tenStxs += timeTxs[i] - tenSgas += timeGas[i] - } - diff := currentTime - startTime - if diff > 0 { - log.Printf( - "[stats] historical TPS: %.2f last 10s TPS: %.2f total txs: %d historical GPS: %.1f, last 10s GPS: %.1f elapsed: %v\n", - float64(totalTxs)/float64(diff), float64(tenStxs)/float64(10), totalTxs, - float64(totalGas)/float64(diff), float64(tenSgas)/float64(10), - time.Duration(diff)*time.Second, - ) - } - } - return ctx.Err() -} diff --git a/cmd/simulator/worker/config.go b/cmd/simulator/worker/config.go deleted file mode 100644 index 5a0a427562..0000000000 --- a/cmd/simulator/worker/config.go +++ /dev/null @@ -1,54 +0,0 @@ -// (c) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package worker - -import ( - "log" - "os" - "path/filepath" - - "sigs.k8s.io/yaml" -) - -type Config struct { - Endpoints []string `json:"endpoints"` - Concurrency int `json:"concurrency"` - BaseFee uint64 `json:"base-fee"` - PriorityFee uint64 `json:"priority-fee"` -} - -// LoadConfig parses and validates the [config] in [.simulator] -func LoadConfig(configPath string) (cfg *Config, err error) { - var d []byte - d, err = os.ReadFile(configPath) - if err != nil { - return nil, err - } - cfg = &Config{} - if err = yaml.Unmarshal(d, cfg); err != nil { - return nil, err - } - log.Printf( - "loaded config (endpoints=%v concurrency=%d base fee=%d priority fee=%d)\n", - cfg.Endpoints, - cfg.BaseFee, - cfg.PriorityFee, - cfg.Concurrency, - ) - return cfg, nil -} - -const fsModeWrite = 0o600 - -func (c *Config) Save(p string) error { - log.Printf("saving config to %q", p) - if err := os.MkdirAll(filepath.Dir(p), fsModeWrite); err != nil { - return err - } - ob, err := yaml.Marshal(c) - if err != nil { - return err - } - return os.WriteFile(p, ob, fsModeWrite) -} diff --git a/cmd/simulator/worker/worker.go b/cmd/simulator/worker/worker.go deleted file mode 100644 index 80eacbce90..0000000000 --- a/cmd/simulator/worker/worker.go +++ /dev/null @@ -1,314 +0,0 @@ -// (c) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package worker - -import ( - "context" - "errors" - "fmt" - "log" - "math/big" - "math/rand" - "time" - - "github.com/ava-labs/subnet-evm/cmd/simulator/key" - "github.com/ava-labs/subnet-evm/cmd/simulator/metrics" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/params" - "golang.org/x/sync/errgroup" -) - -var ( - transferGasLimit = uint64(21000) - transferAmount = big.NewInt(1) - workDelay = time.Duration(100 * time.Millisecond) - retryDelay = time.Duration(500 * time.Millisecond) - - chainID *big.Int - signer types.Signer - feeCap *big.Int - priorityFee *big.Int - - maxTransferCost *big.Int - requestAmount *big.Int - minFunderBalance *big.Int -) - -func setupVars(cID *big.Int, bFee uint64, pFee uint64) { - chainID = cID - signer = types.LatestSignerForChainID(chainID) - priorityFee = new(big.Int).SetUint64(pFee * params.GWei) - feeCap = new(big.Int).Add(new(big.Int).SetUint64(bFee*params.GWei), priorityFee) - - maxTransferCost = new(big.Int).Mul(new(big.Int).SetUint64(transferGasLimit), feeCap) - maxTransferCost = new(big.Int).Add(maxTransferCost, transferAmount) - - requestAmount = new(big.Int).Mul(maxTransferCost, big.NewInt(100)) - minFunderBalance = new(big.Int).Add(maxTransferCost, requestAmount) -} - -func createWorkers(ctx context.Context, keysDir string, endpoints []string, desiredWorkers int) (*worker, []*worker, error) { - keys, err := key.LoadAll(ctx, keysDir) - if err != nil { - return nil, nil, fmt.Errorf("unable to load keys: %w", err) - } - - var master *worker - var workers []*worker - for i, k := range keys { - worker, err := newWorker(k, endpoints[i%len(endpoints)], keysDir) - if err != nil { - return nil, nil, err - } - if err := worker.fetchBalance(ctx); err != nil { - return nil, nil, err - } - if err := worker.fetchNonce(ctx); err != nil { - return nil, nil, fmt.Errorf("could not get nonce: %w", err) - } - log.Printf("loaded worker %s (balance=%s nonce=%d)\n", worker.k.Address.Hex(), worker.balance.String(), worker.nonce) - - switch { - case master == nil: - master = worker - case master.balance.Cmp(worker.balance) < 0: - // We swap the worker with the master if it has a larger balance to - // ensure the master is the key with the largest balance - workers = append(workers, master) - master = worker - default: - workers = append(workers, worker) - } - } - - if master == nil { - master, err = newWorker(nil, endpoints[rand.Intn(len(endpoints))], keysDir) - if err != nil { - return nil, nil, fmt.Errorf("unable to create master: %w", err) - } - } - - for len(workers) < desiredWorkers { - i := len(workers) - worker, err := newWorker(nil, endpoints[i%len(endpoints)], keysDir) - if err != nil { - return nil, nil, fmt.Errorf("unable to create worker: %w", err) - } - - workers = append(workers, worker) - } - - return master, workers[:desiredWorkers], nil -} - -type worker struct { - c *ethclient.Client - k *key.Key - - balance *big.Int - nonce uint64 -} - -func newWorker(k *key.Key, endpoint string, keysDir string) (*worker, error) { - log.Printf("Creating worker endpoint %s, keysDir %s", endpoint, keysDir) - client, err := ethclient.Dial(endpoint) - if err != nil { - return nil, fmt.Errorf("failed to create ethclient: %w", err) - } - - if k == nil { - k, err = key.Generate() - if err != nil { - return nil, fmt.Errorf("problem creating new private key: %w", err) - } - - if err := k.Save(keysDir); err != nil { - return nil, fmt.Errorf("could not save key: %w", err) - } - } - - return &worker{ - c: client, - k: k, - balance: big.NewInt(0), - nonce: 0, - }, nil -} - -func (w *worker) fetchBalance(ctx context.Context) error { - for ctx.Err() == nil { - balance, err := w.c.BalanceAt(ctx, w.k.Address, nil) - if err != nil { - log.Printf("could not get balance: %s\n", err.Error()) - time.Sleep(retryDelay) - continue - } - w.balance = balance - return nil - } - return ctx.Err() -} - -func (w *worker) fetchNonce(ctx context.Context) error { - for ctx.Err() == nil { - nonce, err := w.c.NonceAt(ctx, w.k.Address, nil) - if err != nil { - log.Printf("could not get nonce: %s\n", err.Error()) - time.Sleep(retryDelay) - continue - } - w.nonce = nonce - return nil - } - return ctx.Err() -} - -func (w *worker) waitForBalance(ctx context.Context, stdout bool, minBalance *big.Int) error { - for ctx.Err() == nil { - if err := w.fetchBalance(ctx); err != nil { - return fmt.Errorf("could not get balance: %w", err) - } - - if w.balance.Cmp(minBalance) >= 0 { - if stdout { - log.Printf("found balance of %s\n", w.balance.String()) - } - return nil - } - if stdout { - log.Printf("waiting for balance of %s on %s\n", minBalance.String(), w.k.Address.Hex()) - } - time.Sleep(5 * time.Second) - } - return ctx.Err() -} - -func (w *worker) sendTx(ctx context.Context, recipient common.Address, value *big.Int) error { - for ctx.Err() == nil { - tx := types.NewTx(&types.DynamicFeeTx{ - ChainID: chainID, - Nonce: w.nonce, - To: &recipient, - Gas: transferGasLimit, - GasFeeCap: feeCap, - GasTipCap: priorityFee, - Value: value, - Data: []byte{}, - }) - signedTx, err := types.SignTx(tx, signer, w.k.PrivKey) - if err != nil { - log.Printf("failed to sign transaction: %s", err.Error()) - time.Sleep(retryDelay) - continue - } - if err := w.c.SendTransaction(ctx, signedTx); err != nil { - log.Printf("failed to send transaction: %s", err.Error()) - time.Sleep(retryDelay) - continue - } - txHash := signedTx.Hash() - cost, err := w.confirmTransaction(ctx, txHash) - if err != nil { - log.Printf("failed to confirm %s: %s", txHash.Hex(), err.Error()) - time.Sleep(retryDelay) - continue - } - w.nonce++ - w.balance = new(big.Int).Sub(w.balance, cost) - w.balance = new(big.Int).Sub(w.balance, transferAmount) - return nil - } - return ctx.Err() -} - -func (w *worker) work(ctx context.Context, availableWorkers []*worker, fundRequest chan common.Address) error { - for ctx.Err() == nil { - if w.balance.Cmp(maxTransferCost) < 0 { - log.Printf("%s requesting funds from master\n", w.k.Address.Hex()) - fundRequest <- w.k.Address - if err := w.waitForBalance(ctx, false, maxTransferCost); err != nil { - return fmt.Errorf("could not get balance: %w", err) - } - } - recipient := availableWorkers[rand.Intn(len(availableWorkers))] - if recipient.k.Address == w.k.Address { - continue - } - if err := w.sendTx(ctx, recipient.k.Address, transferAmount); err != nil { - return err - } - time.Sleep(workDelay) - } - return ctx.Err() -} - -func (w *worker) fund(ctx context.Context, fundRequest chan common.Address) error { - for { - select { - case recipient := <-fundRequest: - if w.balance.Cmp(minFunderBalance) < 0 { - if err := w.waitForBalance(ctx, true, minFunderBalance); err != nil { - return fmt.Errorf("could not get minimum balance: %w", err) - } - } - if err := w.sendTx(ctx, recipient, requestAmount); err != nil { - return fmt.Errorf("unable to send tx: %w", err) - } - case <-ctx.Done(): - return ctx.Err() - } - } -} - -func (w *worker) confirmTransaction(ctx context.Context, tx common.Hash) (*big.Int, error) { - for ctx.Err() == nil { - result, pending, _ := w.c.TransactionByHash(ctx, tx) - if result == nil || pending { - time.Sleep(retryDelay) - continue - } - return result.Cost(), nil - } - return nil, ctx.Err() -} - -// Run attempts to apply load to a network specified in .simulator/config.yml -// and periodically prints metrics about the traffic it generates. -func Run(ctx context.Context, cfg *Config, keysDir string) error { - if len(cfg.Endpoints) == 0 { - return errors.New("cannot start worker with no endpoints") - } - rclient, err := ethclient.Dial(cfg.Endpoints[0]) - if err != nil { - return err - } - chainId, err := rclient.ChainID(ctx) - if err != nil { - return err - } - setupVars(chainId, cfg.BaseFee, cfg.PriorityFee) - - master, workers, err := createWorkers(ctx, keysDir, cfg.Endpoints, cfg.Concurrency) - if err != nil { - return fmt.Errorf("unable to load available workers: %w", err) - } - - g, gctx := errgroup.WithContext(ctx) - g.Go(func() error { - return metrics.Monitor(gctx, rclient) - }) - fundRequest := make(chan common.Address) - g.Go(func() error { - return master.fund(gctx, fundRequest) - }) - for _, worker := range workers { - w := worker - g.Go(func() error { - return w.work(gctx, workers, fundRequest) - }) - } - return g.Wait() -} diff --git a/go.mod b/go.mod index c786c1da09..30093221ab 100644 --- a/go.mod +++ b/go.mod @@ -38,11 +38,11 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a github.com/tyler-smith/go-bip39 v1.0.2 github.com/urfave/cli/v2 v2.10.2 - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/crypto v0.1.0 golang.org/x/sync v0.1.0 golang.org/x/sys v0.5.0 golang.org/x/text v0.7.0 - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac + golang.org/x/time v0.1.0 gopkg.in/urfave/cli.v1 v1.20.0 ) diff --git a/go.sum b/go.sum index 995d22e1d6..26cc2af947 100644 --- a/go.sum +++ b/go.sum @@ -619,8 +619,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -824,8 +824,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/scripts/run_simulator.sh b/scripts/run_simulator.sh index 8cd218df0b..d1fb6c4c41 100755 --- a/scripts/run_simulator.sh +++ b/scripts/run_simulator.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# This script runs a 30s load simulation using RPC_ENDPOINTS environment variable to specify +# which RPC endpoints to hit. + set -e echo "Beginning simulator script" @@ -22,18 +25,18 @@ run_simulator() { ################################# echo "building simulator" pushd ./cmd/simulator - go install -v . + go build -o ./simulator main/*.go echo popd echo "running simulator from $PWD" - simulator \ - --rpc-endpoints=$RPC_ENDPOINTS \ - --keys=./cmd/simulator/.simulator/keys \ + ./cmd/simulator/simulator \ + --endpoints=$RPC_ENDPOINTS \ + --key-dir=./cmd/simulator/.simulator/keys \ --timeout=30s \ - --concurrency=10 \ - --base-fee=300 \ - --priority-fee=100 + --workers=1 \ + --max-fee-cap=300 \ + --max-tip-cap=100 } run_simulator