diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 89c50fb963..cefba6cdf5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,12 +6,10 @@ # review whenever someone opens a pull request. -* @ceyonur @darioush @patrick-ogrady @aaronbuchwald +* @ceyonur @darioush @aaronbuchwald -# These owners will be the owner of the contract-examples sub-directory. - -contract-examples/ @dasconnor @anusha-ctrl - -# These owners will be the owner of the scripts sub-directory. +# Specific directories: +peer/ @anusha-ctrl +contract-examples/ @anusha-ctrl scripts/ @anusha-ctrl diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9d43f6b5e1..d0c9fae378 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,35 +1,45 @@ # Contributing -Thank you for considering to help out with the source code! We welcome -contributions from anyone on the internet, and are grateful for even the +Thank you for considering to help out with the source code! We welcome +contributions from anyone on the internet, and are grateful for even the smallest of fixes! -If you'd like to contribute to coreth, please fork, fix, commit and send a +If you'd like to contribute to subnet-evm, please fork, fix, commit and send a pull request for the maintainers to review and merge into the main code base. If -you wish to submit more complex changes though, please check up with the core -devs first on [Discord](https://chat.avalabs.org) to -ensure those changes are in line with the general philosophy of the project +you wish to submit more complex changes though, please check up with the core +devs first on [Discord](https://chat.avalabs.org) to +ensure those changes are in line with the general philosophy of the project and/or get some early feedback which can make both your efforts much lighter as well as our review and merge procedures quick and simple. ## Coding guidelines -Please make sure your contributions adhere to our coding guidelines: - - * Code must adhere to the official Go -[formatting](https://golang.org/doc/effective_go.html#formatting) guidelines -(i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). - * Code must be documented adhering to the official Go -[commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. - * Pull requests need to be based on and opened against the `master` branch. - * Pull reuqests should include a detailed description - * Commits are required to be signed. See [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) - for information on signing commits. - * Commit messages should be prefixed with the package(s) they modify. - * E.g. "eth, rpc: make trace configs optional" +Please make sure your contributions adhere to our coding and documentation +guidelines: + +- Code must adhere to the official Go + [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines + (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). +- Pull requests need to be based on and opened against the `master` branch. +- Pull reuqests should include a detailed description +- Commits are required to be signed. See [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) + for information on signing commits. +- Commit messages should be prefixed with the package(s) they modify. + - E.g. "eth, rpc: make trace configs optional" + +## Documentation guidelines + +- Code should be well commented, so it is easier to read and maintain. + Any complex sections or invariants should be documented explicitly. +- Code must be documented adhering to the official Go + [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. +- Changes with user facing impact (e.g., addition or modification of flags and + options) should be accompanied by a link to a pull request to the [avalanche-docs](https://github.com/ava-labs/avalanche-docs) + repository. [example](https://github.com/ava-labs/avalanche-docs/pull/1119/files). +- Changes that modify a package significantly or add new features should + either update the existing or include a new `README.md` file in that package. ## Can I have feature X -Before you submit a feature request, please check and make sure that it isn't +Before you submit a feature request, please check and make sure that it isn't possible through some other means. - diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md new file mode 100644 index 0000000000..24a1848c87 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -0,0 +1,25 @@ +--- +name: Release Checklist +about: Create a ticket to track a release +title: '' +labels: release +assignees: '' + +--- + +**Release** +The release version and a description of the planned changes to be included in the release. + +**Issues** +Link the major issues planned to be included in the release. + +**Documentation** +Link the relevant documentation PRs for this release. + +**Checklist** +- [ ] Update version in scripts/versions.sh and plugin/evm/version.go +- [ ] Bump AvalancheGo dependency for RPCChainVM Compatibility +- [ ] Add new entry in compatibility.json for RPCChainVM Compatibility +- [ ] Update AvalancheGo compatibility in README +- [ ] Bump cmd/simulator go mod (if needed) +- [ ] Confirm goreleaser job has successfully generated binaries by checking the releases page diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8200c0597f..119cac2eb9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,3 +3,5 @@ ## How this works ## How this was tested + +## How is this documented diff --git a/.github/workflows/lint-tests-release.yml b/.github/workflows/lint-tests-release.yml index 51bf338e0c..5862ddfc2f 100644 --- a/.github/workflows/lint-tests-release.yml +++ b/.github/workflows/lint-tests-release.yml @@ -11,7 +11,7 @@ on: jobs: lint_test: name: Lint - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - run: ./scripts/lint_allowed_geth_imports.sh @@ -63,29 +63,15 @@ jobs: - name: Yarn install run: yarn working-directory: ./contract-examples - - name: Run e2e tests + - name: Install AvalancheGo Release shell: bash - run: SKIP_NETWORK_RUNNER_START=true SKIP_NETWORK_RUNNER_SHUTDOWN=true ENABLE_SOLIDITY_TESTS=true scripts/run.sh - - simulator_test: - name: Load testing with simulator - runs-on: ubuntu-latest - steps: - - name: Git checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - name: Install dependencies with go module + run: BASEDIR=/tmp/e2e-test AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego ./scripts/install_avalanchego_release.sh + - name: Build Subnet-EVM Plugin Binary shell: bash - run: go mod download - - name: Run simulator tests + run: ./scripts/build.sh /tmp/e2e-test/avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy + - name: Run E2E Tests shell: bash - # skip shutdown so external simulator binary can run against the existing cluster - run: SKIP_NETWORK_RUNNER_SHUTDOWN=true RUN_SIMULATOR=true scripts/run.sh + run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego DATA_DIR=/tmp/e2e-test/data ./scripts/run_ginkgo.sh release: # needs: [lint_test, unit_test, e2e_test, simulator_test] diff --git a/README.md b/README.md index c0443cdcf1..e1240d1757 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ The Subnet EVM runs in a separate process from the main AvalancheGo process and [v0.4.2] AvalancheGo@v1.9.1 (Protocol Version: 18) [v0.4.3] AvalancheGo@v1.9.2-v1.9.3 (Protocol Version: 19) [v0.4.4] AvalancheGo@v1.9.2-v1.9.3 (Protocol Version: 19) +[v0.4.5] AvalancheGo@v1.9.4 (Protocol Version: 20) +[v0.4.6] AvalancheGo@v1.9.4 (Protocol Version: 20) +[v0.4.7] AvalancheGo@v1.9.5 (Protocol Version: 21) +[v0.4.8] AvalancheGo@v1.9.6-v1.9.7 (Protocol Version: 22) ``` ## API @@ -74,7 +78,7 @@ To support these changes, there have been a number of changes to the SubnetEVM b ### Clone Subnet-evm -First install Go 1.18.1 or later. Follow the instructions [here](https://golang.org/doc/install). You can verify by runing `go version`. +First install Go 1.18.1 or later. Follow the instructions [here](https://golang.org/doc/install). You can verify by running `go version`. Set `$GOPATH` environment variable properly for Go to look for Go Workspaces. Please read [this](https://go.dev/doc/gopath_code) for details. You can verify by running `echo $GOPATH`. @@ -94,637 +98,9 @@ This will clone and checkout to `master` branch. ### Run Local Network -[`scripts/run.sh`](https://github.com/ava-labs/subnet-evm/blob/master/scripts/run.sh) automatically installs `avalanchego`, sets up a local network, -and creates a `subnet-evm` genesis file. The usage of this script is +To run a local network, it is recommended to use the [avalanche-cli](https://github.com/ava-labs/avalanche-cli#avalanche-cli) to set up an instance of Subnet-EVM on an local Avalanche Network. -```bash -DEFAULT_ACCOUNT=[GENESIS_ADDRESS] ./scripts/run.sh [AVALANCHEGO VERSION] -``` - -```bash -# to startup a local cluster (good for development) -cd ${HOME}/go/src/github.com/ava-labs/subnet-evm -git pull - -# to use the latest version "scripts/versions.sh" -./scripts/run.sh - -# to specify the version and default account for genesis -DEFAULT_ACCOUNT=0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC ./scripts/run.sh 1.9.2 -``` - -Note: make sure you check the version compatibility above between AvalancheGo and Subnet-evm and use the proper version of AvalancheGo. - -Note that this ewoq address (`0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC`) is a prefunded address on the local network, see [here](https://docs.avax.network/quickstart/fund-a-local-test-network) for more info. The private key for this address is -`0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027`. - -With this command, `avalanchego`, `avalanche-network-runner` and GoLang packages will be downloaded and installed on a `/tmp` directory. Note: please make sure that your have fast internet connection to download these packages, otherwise, it will take a long time. - -Once the the network is started up, the following info will be printed to the -console: - -```bash -cluster is ready! - -Logs Directory: /var/folders/0h/v4nrbbsn1vvbr5h2wfrh5h500000gn/T/network-runner-root-data2328077371 - -EVM Chain ID: 99999 -Funded Address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC -RPC Endpoints: -- http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -- http://127.0.0.1:23930/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -- http://127.0.0.1:31984/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -- http://127.0.0.1:41274/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -- http://127.0.0.1:57529/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc - -WS Endpoints: -- ws://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/ws -- ws://127.0.0.1:23930/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/ws -- ws://127.0.0.1:31984/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/ws -- ws://127.0.0.1:41274/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/ws -- ws://127.0.0.1:57529/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/ws - -MetaMask Quick Start: -Funded Address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC -Network Name: Local EVM -RPC URL: http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -Chain ID: 99999 -Curreny Symbol: LEVM -network-runner RPC server is running on PID 79100... - -use the following command to terminate: - -pkill -P 79100 -kill -2 79100 -pkill -9 -f srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy -``` - -You can then ping the local cluster or add the network to MetaMask: - -```bash -curl --location --request POST 'http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc' \ ---header 'Content-Type: application/json' \ ---data-raw '{ - "jsonrpc": "2.0", - "method": "eth_blockNumber", - "params":[], - "id": 1 -}' -``` - -Response: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "result": "0x0" -} -``` - -To terminate the cluster, run the following commands: - -```bash -pkill -P 79100 -kill -2 79100 -pkill -9 -f srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy -``` - -### Connect with Metamask - -Please use the value provided by `MetaMask Quick Start` to connect with Metamask. - -```text -MetaMask Quick Start: -Funded Address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC -Network Name: Local EVM -RPC URL: http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc -Chain ID: 99999 -Curreny Symbol: LEVM -``` - -You can create a new metamask account by importing the private key `0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027` and start experiencing with this account. - -### Load Simulator - -When building developing your own blockchain using `subnet-evm`, you may want -to analyze how your fee paramterization behaves and/or how many resources your VM -uses under different load patterns. For this reason, we developed `cmd/simulator`. -`cmd/simulator` lets your drive arbitrary load across any number of [endpoints] -with a user-specified `concurrency`, `base-fee`, and `priority-fee`. - -To get started, open the directory `cmd/simulator` and add your network's endpoints to -the file at `.simulator/config.yml` (these will be provided after running -`./scripts/run.sh`. With the example above, the correct endpoints is `http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc` to replace `http://localhost:9650/ext/bc/my-chain/rpc`.): - -```yaml -endpoints: - - http://localhost:9650/ext/bc/my-chain/rpc -base-fee: 25 -priority-fee: 1 -concurrency: 10 -``` +There are two options when using the Avalanche-CLI: -Once your config is specified, you can run the tool by either invoking `go run main.go` under the directory `cmd/simulator` or by installing the tool (`go install -v .`) and running the binary -(`simulator`). - -To make getting started easier, the ewoq key `0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC` -has been pre-added to the simulator key directory and can be added to genesis during local network -creation (`DEFAULT_ACCOUNT=[GENESIS_ADDRESS] ./scripts/run.sh [AVALANCHEGO VERSION]`). -If you do not add this key to genesis, you'll need to manually fund the -`master` account when prompted in the terminal. - -_The private key for the ewoq address (`0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC`) is -`0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027`._ - -If you followed the directions successfully, you should see the following: - -```bash -> go run main.go -go: downloading github.com/ava-labs/subnet-evm v0.1.2 -go: downloading github.com/spf13/viper v1.10.1 -2022/05/11 09:49:22 loaded config (endpoints=[http://127.0.0.1:14463/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc] concurrency=25 base fee=1 priority fee=10) -2022/05/11 09:49:22 loaded worker 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC (balance=100000000000000000000000000 nonce=0) -2022/05/11 09:49:22 0xe8859AF6c05b512dF80A66b81dE89FDAB9fE5C1c requesting funds from master -2022/05/11 09:49:22 0xa2B32bcbA31d4dC7728aD73165cdeea5eCeD5e70 requesting funds from master -2022/05/11 09:49:22 0x837438175627A7A2ABbccf1727c5cA46fA7274b5 requesting funds from master -2022/05/11 09:49:22 0x14c908A82047C6bC66cd9282b4D68f3e003659f8 requesting funds from master -2022/05/11 09:49:22 0xbeE6DF853592d3699ac3292D134F59BEF278B048 requesting funds from master -2022/05/11 09:49:22 0x028Bc164dcC1c10f1Db5a1175c58eA84a7Fd34c9 requesting funds from master -2022/05/11 09:49:22 0x664D97348Bdb73fc3bC4447B4676573dbF6eEE5A requesting funds from master -2022/05/11 09:49:22 0x455aAB371261DC41a048e42Bf147ced4FaDE5fCF requesting funds from master -2022/05/11 09:49:22 0xA9b5C64E057F50730CA4Ba6205d55fa08C03ff75 requesting funds from master -2022/05/11 09:49:22 0x57645A2bdCEb6cFbC95e6a5Cac70F0c05B8d8515 requesting funds from master -2022/05/11 09:49:24 [block created] t: 2022-05-11 09:49:22 -0600 MDT index: 1 base fee: 1 block gas cost: 0 block txs: 1 gas used: 21000 -2022/05/11 09:49:24 [block created] t: 2022-05-11 09:49:24 -0600 MDT index: 2 base fee: 1 block gas cost: 0 block txs: 1 gas used: 21000 -2022/05/11 09:49:24 [stats] historical TPS: 1.00 last 10s TPS: 0.10 total txs: 2 historical GPS: 21000.0, last 10s GPS: 2100.0 elapsed: 2s -2022/05/11 09:49:26 [block created] t: 2022-05-11 09:49:26 -0600 MDT index: 3 base fee: 1 block gas cost: 0 block txs: 1 gas used: 21000 -2022/05/11 09:49:26 [stats] historical TPS: 0.75 last 10s TPS: 0.20 total txs: 3 historical GPS: 15750.0, last 10s GPS: 4200.0 elapsed: 4s -2022/05/11 09:49:28 [block created] t: 2022-05-11 09:49:28 -0600 MDT index: 4 base fee: 1 block gas cost: 0 block txs: 2 gas used: 42000 -2022/05/11 09:49:28 [stats] historical TPS: 0.83 last 10s TPS: 0.30 total txs: 5 historical GPS: 17500.0, last 10s GPS: 6300.0 elapsed: 6s -2022/05/11 09:49:30 [block created] t: 2022-05-11 09:49:30 -0600 MDT index: 5 base fee: 1 block gas cost: 0 block txs: 4 gas used: 84000 -2022/05/11 09:49:30 [stats] historical TPS: 1.12 last 10s TPS: 0.50 total txs: 9 historical GPS: 23625.0, last 10s GPS: 10500.0 elapsed: 8s -2022/05/11 09:49:32 [block created] t: 2022-05-11 09:49:32 -0600 MDT index: 6 base fee: 1 block gas cost: 0 block txs: 5 gas used: 105000 -2022/05/11 09:49:32 [stats] historical TPS: 1.40 last 10s TPS: 0.90 total txs: 14 historical GPS: 29400.0, last 10s GPS: 18900.0 elapsed: 10s -2022/05/11 09:49:34 [block created] t: 2022-05-11 09:49:34 -0600 MDT index: 7 base fee: 1 block gas cost: 0 block txs: 6 gas used: 126000 -2022/05/11 09:49:34 [stats] historical TPS: 1.67 last 10s TPS: 1.30 total txs: 20 historical GPS: 35000.0, last 10s GPS: 27300.0 elapsed: 12s -2022/05/11 09:49:36 [block created] t: 2022-05-11 09:49:36 -0600 MDT index: 8 base fee: 1 block gas cost: 0 block txs: 7 gas used: 147000 -2022/05/11 09:49:36 [stats] historical TPS: 1.93 last 10s TPS: 1.80 total txs: 27 historical GPS: 40500.0, last 10s GPS: 37800.0 elapsed: 14s -2022/05/11 09:49:38 [block created] t: 2022-05-11 09:49:38 -0600 MDT index: 9 base fee: 1 block gas cost: 0 block txs: 8 gas used: 168000 -2022/05/11 09:49:38 [stats] historical TPS: 2.19 last 10s TPS: 2.40 total txs: 35 historical GPS: 45937.5, last 10s GPS: 50400.0 elapsed: 16s -2022/05/11 09:49:40 [block created] t: 2022-05-11 09:49:40 -0600 MDT index: 10 base fee: 1 block gas cost: 0 block txs: 9 gas used: 189000 -2022/05/11 09:49:40 [stats] historical TPS: 2.44 last 10s TPS: 3.00 total txs: 44 historical GPS: 51333.3, last 10s GPS: 63000.0 elapsed: 18s -2022/05/11 09:49:42 [block created] t: 2022-05-11 09:49:42 -0600 MDT index: 11 base fee: 1 block gas cost: 0 block txs: 9 gas used: 189000 -2022/05/11 09:49:42 [stats] historical TPS: 2.65 last 10s TPS: 3.50 total txs: 53 historical GPS: 55650.0, last 10s GPS: 73500.0 elapsed: 20s -2022/05/11 09:49:44 [block created] t: 2022-05-11 09:49:44 -0600 MDT index: 12 base fee: 1 block gas cost: 0 block txs: 10 gas used: 210000 -2022/05/11 09:49:44 [stats] historical TPS: 2.86 last 10s TPS: 3.90 total txs: 63 historical GPS: 60136.4, last 10s GPS: 81900.0 elapsed: 22s -2022/05/11 09:49:46 [block created] t: 2022-05-11 09:49:46 -0600 MDT index: 13 base fee: 1 block gas cost: 0 block txs: 10 gas used: 210000 -2022/05/11 09:49:46 [stats] historical TPS: 3.04 last 10s TPS: 4.30 total txs: 73 historical GPS: 63875.0, last 10s GPS: 90300.0 elapsed: 24s -..... - -2022/05/11 09:55:51 [stats] historical TPS: 4.89 last 10s TPS: 5.00 total txs: 1896 historical GPS: 102618.6, last 10s GPS: 105000.0 elapsed: 6m28s -2022/05/11 09:55:52 0xa2B32bcbA31d4dC7728aD73165cdeea5eCeD5e70 requesting funds from master -2022/05/11 09:55:53 [block created] t: 2022-05-11 09:55:52 -0600 MDT index: 196 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:55:53 [stats] historical TPS: 4.89 last 10s TPS: 5.10 total txs: 1907 historical GPS: 102684.6, last 10s GPS: 107100.0 elapsed: 6m30s -2022/05/11 09:55:54 0x14c908A82047C6bC66cd9282b4D68f3e003659f8 requesting funds from master -2022/05/11 09:55:55 [block created] t: 2022-05-11 09:55:54 -0600 MDT index: 197 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:55:55 [stats] historical TPS: 4.89 last 10s TPS: 5.20 total txs: 1918 historical GPS: 102750.0, last 10s GPS: 109200.0 elapsed: 6m32s -2022/05/11 09:55:56 0xbeE6DF853592d3699ac3292D134F59BEF278B048 requesting funds from master -2022/05/11 09:55:57 [block created] t: 2022-05-11 09:55:56 -0600 MDT index: 198 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:55:57 [stats] historical TPS: 4.90 last 10s TPS: 5.30 total txs: 1929 historical GPS: 102814.7, last 10s GPS: 111300.0 elapsed: 6m34s -2022/05/11 09:55:58 0x028Bc164dcC1c10f1Db5a1175c58eA84a7Fd34c9 requesting funds from master -2022/05/11 09:55:59 [block created] t: 2022-05-11 09:55:58 -0600 MDT index: 199 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:55:59 [stats] historical TPS: 4.90 last 10s TPS: 5.40 total txs: 1940 historical GPS: 102878.8, last 10s GPS: 113400.0 elapsed: 6m36s -2022/05/11 09:56:00 0x664D97348Bdb73fc3bC4447B4676573dbF6eEE5A requesting funds from master -2022/05/11 09:56:01 [block created] t: 2022-05-11 09:56:00 -0600 MDT index: 200 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:56:01 [stats] historical TPS: 4.90 last 10s TPS: 5.50 total txs: 1951 historical GPS: 102942.2, last 10s GPS: 115500.0 elapsed: 6m38s -2022/05/11 09:56:02 0x455aAB371261DC41a048e42Bf147ced4FaDE5fCF requesting funds from master -2022/05/11 09:56:03 [block created] t: 2022-05-11 09:56:02 -0600 MDT index: 201 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:56:03 [stats] historical TPS: 4.91 last 10s TPS: 5.50 total txs: 1962 historical GPS: 103005.0, last 10s GPS: 115500.0 elapsed: 6m40s -2022/05/11 09:56:04 0xA9b5C64E057F50730CA4Ba6205d55fa08C03ff75 requesting funds from master -2022/05/11 09:56:05 [block created] t: 2022-05-11 09:56:04 -0600 MDT index: 202 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:56:05 [stats] historical TPS: 4.91 last 10s TPS: 5.50 total txs: 1973 historical GPS: 103067.2, last 10s GPS: 115500.0 elapsed: 6m42s -2022/05/11 09:56:06 0x57645A2bdCEb6cFbC95e6a5Cac70F0c05B8d8515 requesting funds from master -2022/05/11 09:56:07 [block created] t: 2022-05-11 09:56:06 -0600 MDT index: 203 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:56:07 [stats] historical TPS: 4.91 last 10s TPS: 5.50 total txs: 1984 historical GPS: 103128.7, last 10s GPS: 115500.0 elapsed: 6m44s -2022/05/11 09:56:09 [block created] t: 2022-05-11 09:56:08 -0600 MDT index: 204 base fee: 1 block gas cost: 0 block txs: 11 gas used: 231000 -2022/05/11 09:56:09 [stats] historical TPS: 4.91 last 10s TPS: 5.50 total txs: 1995 historical GPS: 103189.7, last 10s GPS: 115500.0 elapsed: 6m46s -2022/05/11 09:56:11 [block created] t: 2022-05-11 09:56:10 -0600 MDT index: 205 base fee: 1 block gas cost: 0 block txs: 10 gas used: 210000 -2022/05/11 09:56:11 [stats] historical TPS: 4.91 last 10s TPS: 5.50 total txs: 2005 historical GPS: 103198.5, last 10s GPS: 115500.0 elapsed: 6m48s -2022/05/11 09:56:13 [block created] t: 2022-05-11 09:56:12 -0600 MDT index: 206 base fee: 1 block gas cost: 0 block txs: 10 gas used: 210000 -2022/05/11 09:56:13 [stats] historical TPS: 4.91 last 10s TPS: 5.40 total txs: 2015 historical GPS: 103207.3, last 10s GPS: 113400.0 elapsed: 6m50s -``` - -## Create an EVM Subnet on Fuji Testnet - -See [this tutorial](https://docs.avax.network/subnets/create-a-fuji-subnet-subnet-cli). - -## Customize a Subnet - -- [Genesis](https://docs.avax.network/subnets/customize-a-subnet#genesis) -- [Precompile](https://docs.avax.network/subnets/customize-a-subnet#precompiles) -- [Priority Regossip](https://docs.avax.network/subnets/customize-a-subnet#priority-regossip) - -## Join the WAGMI Subnet Demo - -

- WAGMI -

- -_Thanks to the @0xNeonMonsters for the logo!_ - -The WAGMI ("We're All Going to Make It") Subnet Demo is a high throughput -testbed for EVM (Ethereum Virtual Machine) optimizations. It is parameterized -to run at a factor more capacity than Fuji/Mainnet C-Chain and will be used -to experiment with release candidates before they make it into an -official [`coreth`](https://github.com/ava-labs/coreth) release. - -We created a basic [WAGMI explorer](https://trywagmi.xyz) that surfaces -aggregated usage statistics about the subnet. If you'd like to see any other -stats added to this site, please send a DM to [@\_patrickogrady on Twitter](https://twitter.com/_patrickogrady). - -Everyone that has used the the C-Chain more than twice (~970k addresses) has -been airdropped 10 WGM tokens. With the current fee parameterization, this -should be enough for hundreds of txs. - -This is one of the first cases of using Avalanche Subnets as a proving ground -for changes in a production VM (coreth). Many underestimate how useful the isolation -of subnets is for performing complex VM testing on a live network (without impacting -the stability of the primary network). - -### Network Creation - -To create WAGMI, all we had to do was run the following command: - -```bash -subnet-cli wizard \ ---node-ids=NodeID-9TCq8np31pHjjhGaHtLjs6ptYYPEt3LGb,NodeID-BrYXghQSu6KKGjuzhs3nrkcB46Wc2yYHy,NodeID-89UCR1CsPzzEHuknxhJHKxuFPNCyPz7Bu,NodeID-Hfm8gpD4DpCz4KTzt2osJPfFvu7az3qiD,NodeID-LkdxkfYhg6nSw1EEUxDUSYPXPwmr2cUet \ ---vm-genesis-path=networks/11111/genesis.json \ ---vm-id=srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy \ ---chain-name=wagmi -``` - -This added these NodeIDs as validators on Fuji, created the WAGMI Subnet, added -all validators to the WAGMI subnet, and created the WAGMI chain. - -- SubnetID: [28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY](https://testnet.avascan.info/blockchains?subnet=28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY) -- ChainID: [2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt](https://testnet.avascan.info/blockchain/2ebCneCbwthjQ1rYT41nhd7M76Hc6YmosMAQrTFhBq8qeqh6tt) - -### Network Parameters - -```text -Network ID: 11111 -Chain ID: 11111 -Block Gas Limit: 20,000,000 (2.5x C-Chain) -10s Gas Target: 100,000,000 (~6.67x C-Chain) -Min Fee: 1 GWei (4% of C-Chain) -Target Block Rate: 2s (Same as C-Chain) -``` - -### Adding to MetaMask - -```text -Network Name: WAGMI -RPC URL: https://subnets.avax.network/wagmi/wagmi-chain-testnet/rpc -Chain ID: 11111 -Symbol: WGM -Explorer: https://subnets.avax.network/wagmi/wagmi-chain-testnet/explorer -``` - -![metamask_WAGMI](./imgs/metamask_WAGMI.png) - -### Wrapped WAGMI - -#### Info - -```text -Address: 0x3Ee7094DADda15810F191DD6AcF7E4FFa37571e4 -IPFS: /ipfs/QmVAuheeidjD2ktdX3sSHMQqSfcjtmca1g9jr7w9GQf7pU -``` - -#### Metadata - -```json -{ - "compiler": { "version": "0.5.17+commit.d19bba13" }, - "language": "Solidity", - "output": { - "abi": [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "src", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "guy", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "wad", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "dst", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "wad", - "type": "uint256" - } - ], - "name": "Deposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "src", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "dst", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "wad", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "src", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "wad", - "type": "uint256" - } - ], - "name": "Withdrawal", - "type": "event" - }, - { "payable": true, "stateMutability": "payable", "type": "fallback" }, - { - "constant": true, - "inputs": [ - { "internalType": "address", "name": "", "type": "address" }, - { "internalType": "address", "name": "", "type": "address" } - ], - "name": "allowance", - "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "internalType": "address", "name": "guy", "type": "address" }, - { "internalType": "uint256", "name": "wad", "type": "uint256" } - ], - "name": "approve", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { "internalType": "address", "name": "", "type": "address" } - ], - "name": "balanceOf", - "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [], - "name": "deposit", - "outputs": [], - "payable": true, - "stateMutability": "payable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [{ "internalType": "string", "name": "", "type": "string" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ - { "internalType": "uint256", "name": "", "type": "uint256" } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "internalType": "address", "name": "dst", "type": "address" }, - { "internalType": "uint256", "name": "wad", "type": "uint256" } - ], - "name": "transfer", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "internalType": "address", "name": "src", "type": "address" }, - { "internalType": "address", "name": "dst", "type": "address" }, - { "internalType": "uint256", "name": "wad", "type": "uint256" } - ], - "name": "transferFrom", - "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "internalType": "uint256", "name": "wad", "type": "uint256" } - ], - "name": "withdraw", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - } - ], - "devdoc": { "methods": {} }, - "userdoc": { "methods": {} } - }, - "settings": { - "compilationTarget": { "contracts/wwagmi.sol": "WWAGMI" }, - "evmVersion": "istanbul", - "libraries": {}, - "optimizer": { "enabled": false, "runs": 200 }, - "remappings": [] - }, - "sources": { - "contracts/wwagmi.sol": { - "keccak256": "0x0a6ce5559225d3c99db4a5e24777049df3c84886ba9a08147f23afae4261b509", - "urls": [ - "bzz-raw://0aef254c65ae30b578256a7e2496ed18bf0cb68e97f5831050e17a2cf0192a7e", - "dweb:/ipfs/QmSwAbdnaYvrjDHTKnE3qBZ3smT7uipSSfSGBUiKWmNWEY" - ] - } - }, - "version": 1 -} -``` - -#### Code - -```solidity -// Copyright (C) 2015, 2016, 2017 Dapphub - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// Contract name, token name, and token symbol modified by Ava Labs 2020 - -pragma solidity >=0.4.22 <0.6; - -contract WWAGMI{ - string public name = "Wrapped WAGMI"; - string public symbol = "WWAGMI"; - uint8 public decimals = 18; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - event Deposit(address indexed dst, uint wad); - event Withdrawal(address indexed src, uint wad); - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - function() external payable { - deposit(); - } - function deposit() public payable { - balanceOf[msg.sender] += msg.value; - emit Deposit(msg.sender, msg.value); - } - function withdraw(uint wad) public { - require(balanceOf[msg.sender] >= wad); - balanceOf[msg.sender] -= wad; - msg.sender.transfer(wad); - emit Withdrawal(msg.sender, wad); - } - - function totalSupply() public view returns (uint) { - return address(this).balance; - } - - function approve(address guy, uint wad) public returns (bool) { - allowance[msg.sender][guy] = wad; - emit Approval(msg.sender, guy, wad); - return true; - } - - function transfer(address dst, uint wad) public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint wad) - public - returns (bool) - { - require(balanceOf[src] >= wad); - - if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { - require(allowance[src][msg.sender] >= wad); - allowance[src][msg.sender] -= wad; - } - - balanceOf[src] -= wad; - balanceOf[dst] += wad; - - emit Transfer(src, dst, wad); - - return true; - } -} -``` +1. Use an official Subnet-EVM release: https://docs.avax.network/subnets/build-first-subnet +2. Build and deploy a locally built (and optionally modified) version of Subnet-EVM: https://docs.avax.network/subnets/create-custom-subnet diff --git a/accounts/abi/bind/precompile_contract_template.go b/accounts/abi/bind/precompile_contract_template.go index 47c3a04188..709393da6d 100644 --- a/accounts/abi/bind/precompile_contract_template.go +++ b/accounts/abi/bind/precompile_contract_template.go @@ -41,16 +41,16 @@ Typically, custom codes are required in only those areas. 8- Write solidity tests for your precompile in contract-examples/test 9- Create your genesis with your precompile enabled in tests/e2e/genesis/ 10- Create e2e test for your solidity test in tests/e2e/solidity/suites.go -11- Run your e2e precompile Solidity tests with 'E2E=true ./scripts/run.sh' - +11- Run your e2e precompile Solidity tests with './scripts/run_ginkgo.sh' */ package {{.Package}} import ( - "math/big" + "encoding/json" "errors" "fmt" + "math/big" "strings" "github.com/ava-labs/subnet-evm/accounts/abi" diff --git a/cmd/simulator/.gitignore b/cmd/simulator/.gitignore new file mode 100644 index 0000000000..c4408cd691 --- /dev/null +++ b/cmd/simulator/.gitignore @@ -0,0 +1,34 @@ +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 new file mode 100644 index 0000000000..64a619dca0 --- /dev/null +++ b/cmd/simulator/README.md @@ -0,0 +1,89 @@ +# 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`. + +## Building the Load Simulator + +To build the load simulator, navigate to the base of the simulator directory: + +```bash +cd $GOPATH/src/github.com/ava-labs/subnet-evm/cmd/simulator +``` + +Build the simulator: + +```bash +go build -o ./simulator *.go +``` + +To confirm that you built successfully, run the simulator and print the version: + +```bash +./simulator -v +``` + +This should give the following output: + +``` +v0.0.1 +``` + +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. + +To start a single node network, follow the instructions from the AvalancheGo [README](https://github.com/ava-labs/avalanchego#building-avalanchego) to build from source. + +Once you've built AvalancheGo, open the AvalancheGo directory in a separate terminal window and run a single node non-staking network with the following command: + +```bash +./build/avalanchego --staking-enabled=false --network-id=local +``` + +:::warning +The staking-enabled flag is only for local testing. Disabling staking serves two functions explicitly for testing purposes: + +1. Ignore stake weight on the P-Chain and count each connected peer as having a stake weight of 1 +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. + +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 +``` + +## Command Line Flags + +### `rpc-endpoints` (string) + +`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). diff --git a/cmd/simulator/go.mod b/cmd/simulator/go.mod index b5144e9a0b..df7d5c7240 100644 --- a/cmd/simulator/go.mod +++ b/cmd/simulator/go.mod @@ -3,78 +3,43 @@ module github.com/ava-labs/subnet-evm/cmd/simulator go 1.18 require ( - github.com/ava-labs/subnet-evm v0.0.0-00010101000000-000000000000 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 ) -// always depend on the local version -replace github.com/ava-labs/subnet-evm => ../.. - require ( github.com/VictoriaMetrics/fastcache v1.10.0 // indirect - github.com/ava-labs/avalanchego v1.9.5 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // 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/go-logr/logr v1.2.3 // indirect - github.com/go-logr/stdr v1.2.2 // 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/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.2.0 // indirect - github.com/gorilla/rpc v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.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/supranational/blst v0.3.11-0.20220920110316-f72618070295 // 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 - go.opentelemetry.io/otel v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 // indirect - go.opentelemetry.io/otel/sdk v1.11.0 // indirect - go.opentelemetry.io/otel/trace v1.11.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.23.0 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // 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 - gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.51.0-dev // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // 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 - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cmd/simulator/go.sum b/cmd/simulator/go.sum index ad5ceadffe..4a262340f5 100644 --- a/cmd/simulator/go.sum +++ b/cmd/simulator/go.sum @@ -1,80 +1,22 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/ava-labs/avalanchego v1.9.5 h1:0uykbcKFocUL7U7SO/PGrXSMLX9RSt8xo5rj84bI3YY= -github.com/ava-labs/avalanchego v1.9.5/go.mod h1:1f/z4CBcz/VhNlOTj607dQj5ZQQaZQO/RO8sEa0EgvA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE= -github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +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/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +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.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +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= @@ -85,139 +27,60 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +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.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/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/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= -github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= +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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 h1:kr3j8iIMR4ywO/O0rvksXaJvauGGCMg2zAZIiNZ9uIQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0/go.mod h1:ummNFgdgLhhX7aIiy35vVmQNS0rWXknfPE0qe6fmFXg= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +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/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/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= @@ -225,102 +88,70 @@ 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/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 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 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 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/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= 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/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 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/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +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/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/supranational/blst v0.3.11-0.20220920110316-f72618070295 h1:rVKS9JjtqE4/PscoIsP46sRnJhfq8YFbjlk0fUJTRnY= -github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 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= @@ -328,387 +159,103 @@ github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITn 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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk= -go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 h1:0dly5et1i/6Th3WHn0M6kYiJfFNzhhxanrJ0bOfnjEo= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0/go.mod h1:+Lq4/WkdCkjbGcBMVHHg2apTbv8oMBf29QCnyCCJjNQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 h1:eyJ6njZmH16h9dOKCi7lMswAnGsSOwgTqWzfxqcuNr8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0/go.mod h1:FnDp7XemjN3oZ3xGunnfOUTVwd2XcvLbtRAuOSU3oc8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 h1:j2RFV0Qdt38XQ2Jvi4WIsQ56w8T7eSirYbMw19VXRDg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0/go.mod h1:pILgiTEtrqvZpoiuGdblDgS5dbIaTgDrkIuKfEFkt+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0 h1:v29I/NbVp7LXQYMFZhU6q17D0jSEbYOAVONlrO1oH5s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.0/go.mod h1:/RpLsmbQLDO1XCbWAM4S6TSwj8FKwwgyKKyqtvVfAnw= -go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo= -go.opentelemetry.io/otel/sdk v1.11.0/go.mod h1:REusa8RsyKaq0OlyangWXaw97t2VogoO4SSEeKkSTAk= -go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtTAOVFI= -go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= 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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 h1:rxKZ2gOnYxjfmakvUUqh9Gyb6KXfrj7JWTxORTYqb0E= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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-20190227155943-e225da77a7e6/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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-20191001151750-bb3f8db39f24/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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220114195835-da31bd327af9/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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/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/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/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.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.51.0-dev h1:JIZpGUpbGAukP4rGiKJ/AnpK9BqMYV6Rdx94XWZckHY= -google.golang.org/grpc v1.51.0-dev/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/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.0-20210107192922-496545a6307b/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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 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/main.go b/cmd/simulator/main.go index 570e27121b..2063cf471c 100644 --- a/cmd/simulator/main.go +++ b/cmd/simulator/main.go @@ -15,7 +15,6 @@ import ( "github.com/ava-labs/subnet-evm/cmd/simulator/worker" "github.com/spf13/cobra" - "sigs.k8s.io/yaml" ) func init() { @@ -31,15 +30,24 @@ func main() { } var ( - timeout time.Duration - keysDir string + version = "v0.0.1" + versionFlag bool + timeout time.Duration + keysDir string - clusterInfoYamlPath string - rpcEndpoints []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 { @@ -50,20 +58,25 @@ func newCommand() *cobra.Command { 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 for key files") - cmd.PersistentFlags().StringVarP(&clusterInfoYamlPath, "cluster-info-yaml", "o", "", "If non-empty, it loads the endpoints from the YAML and overwrites --config") - cmd.PersistentFlags().StringSliceVarP(&rpcEndpoints, "endpoints", "e", nil, "If non-empty, it loads the endpoints from the YAML and overwrites --config") - cmd.PersistentFlags().IntVarP(&concurrency, "concurrency", "c", 10, "Concurrency") - cmd.PersistentFlags().Uint64VarP(&baseFee, "base-fee", "f", 25, "Base fee") - cmd.PersistentFlags().Uint64VarP(&priorityFee, "priority-fee", "p", 1, "Base fee") + 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) { - log.Printf("launching simulator with rpc endpoints %q or cluster info yaml %q, timeout %v, concurrentcy %d, base fee %d, priority fee %d", - rpcEndpoints, clusterInfoYamlPath, timeout, concurrency, baseFee, priorityFee) + 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, @@ -72,35 +85,6 @@ func runFunc(cmd *cobra.Command, args []string) { PriorityFee: priorityFee, } - if clusterInfoYamlPath != "" { - log.Printf("loading cluster info yaml %q", clusterInfoYamlPath) - b, err := os.ReadFile(clusterInfoYamlPath) - if err != nil { - log.Fatalf("failed to read cluster info yaml %v", err) - } - var ci networkRunnerClusterInfo - if err = yaml.Unmarshal(b, &ci); err != nil { - log.Fatalf("failed to parse cluster info yaml %v", err) - } - - eps := make([]string, len(ci.URIs)) - for i := range eps { - /* - e.g., - - uris: - - http://127.0.0.1:32945 - - http://127.0.0.1:38948 - - http://127.0.0.1:47203 - - http://127.0.0.1:54708 - - http://127.0.0.1:64435 - endpoint: /ext/bc/oFzgVk4nzHApgcBAPXa7JLX5mhqAJnxQkiYD915tZ6LMPcPRu - */ - eps[i] = ci.URIs[i] + ci.Endpoint + "/rpc" - } - cfg.Endpoints = eps - } - ctx, cancel := context.WithTimeout(context.Background(), timeout) errc := make(chan error) go func() { @@ -120,8 +104,3 @@ func runFunc(cmd *cobra.Command, args []string) { } } } - -type networkRunnerClusterInfo struct { - URIs []string `json:"uris"` - Endpoint string `json:"endpoint"` -} diff --git a/cmd/simulator/metrics/metrics.go b/cmd/simulator/metrics/metrics.go index 821de7f28e..4df658164a 100644 --- a/cmd/simulator/metrics/metrics.go +++ b/cmd/simulator/metrics/metrics.go @@ -10,9 +10,9 @@ import ( "math/big" "time" - "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/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" ) const ( @@ -21,7 +21,7 @@ const ( // Monitor periodically prints metrics related to transaction activity on // a given network. -func Monitor(ctx context.Context, client ethclient.Client) error { +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) @@ -58,7 +58,7 @@ func Monitor(ctx context.Context, client ethclient.Client) error { gas := block.GasUsed() t := block.Time() - log.Printf("[block created] t: %v index: %d base fee: %d block gas cost: %d block txs: %d gas used: %d\n", time.Unix(int64(t), 0), i, block.BaseFee().Div(block.BaseFee(), big.NewInt(params.GWei)), block.BlockGasCost(), txs, gas) + 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 diff --git a/cmd/simulator/worker/worker.go b/cmd/simulator/worker/worker.go index b398ec0988..80eacbce90 100644 --- a/cmd/simulator/worker/worker.go +++ b/cmd/simulator/worker/worker.go @@ -5,6 +5,7 @@ package worker import ( "context" + "errors" "fmt" "log" "math/big" @@ -13,10 +14,10 @@ import ( "github.com/ava-labs/subnet-evm/cmd/simulator/key" "github.com/ava-labs/subnet-evm/cmd/simulator/metrics" - "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/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" "golang.org/x/sync/errgroup" ) @@ -104,7 +105,7 @@ func createWorkers(ctx context.Context, keysDir string, endpoints []string, desi } type worker struct { - c ethclient.Client + c *ethclient.Client k *key.Key balance *big.Int @@ -112,6 +113,7 @@ type worker struct { } 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) @@ -276,6 +278,9 @@ func (w *worker) confirmTransaction(ctx context.Context, tx common.Hash) (*big.I // 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 diff --git a/commontype/fee_config.go b/commontype/fee_config.go index 0e4179e0d1..d7d23f3f32 100644 --- a/commontype/fee_config.go +++ b/commontype/fee_config.go @@ -61,6 +61,23 @@ var EmptyFeeConfig = FeeConfig{} // Verify checks fields of this config to ensure a valid fee configuration is provided. func (f *FeeConfig) Verify() error { + switch { + case f.GasLimit == nil: + return fmt.Errorf("gasLimit cannot be nil") + case f.MinBaseFee == nil: + return fmt.Errorf("minBaseFee cannot be nil") + case f.TargetGas == nil: + return fmt.Errorf("targetGas cannot be nil") + case f.BaseFeeChangeDenominator == nil: + return fmt.Errorf("baseFeeChangeDenominator cannot be nil") + case f.MinBlockGasCost == nil: + return fmt.Errorf("minBlockGasCost cannot be nil") + case f.MaxBlockGasCost == nil: + return fmt.Errorf("maxBlockGasCost cannot be nil") + case f.BlockGasCostStep == nil: + return fmt.Errorf("blockGasCostStep cannot be nil") + } + switch { case f.GasLimit.Cmp(common.Big0) != 1: return fmt.Errorf("gasLimit = %d cannot be less than or equal to 0", f.GasLimit) diff --git a/commontype/fee_config_test.go b/commontype/fee_config_test.go index 57c8a58b06..997cb708e3 100644 --- a/commontype/fee_config_test.go +++ b/commontype/fee_config_test.go @@ -29,6 +29,22 @@ func TestVerify(t *testing.T) { config *FeeConfig expectedError string }{ + { + name: "nil gasLimit in FeeConfig", + config: &FeeConfig{ + // GasLimit: big.NewInt(8_000_000) + TargetBlockRate: 2, // in seconds + + MinBaseFee: big.NewInt(25_000_000_000), + TargetGas: big.NewInt(15_000_000), + BaseFeeChangeDenominator: big.NewInt(36), + + MinBlockGasCost: big.NewInt(0), + MaxBlockGasCost: big.NewInt(1_000_000), + BlockGasCostStep: big.NewInt(200_000), + }, + expectedError: "gasLimit cannot be nil", + }, { name: "invalid GasLimit in FeeConfig", config: func() *FeeConfig { c := validFeeConfig; c.GasLimit = big.NewInt(0); return &c }(), diff --git a/compatibility.json b/compatibility.json index 7caa22e49e..f9d90ad393 100644 --- a/compatibility.json +++ b/compatibility.json @@ -1,5 +1,7 @@ { "rpcChainVMProtocolVersion": { + "v0.4.9": 22, + "v0.4.8": 22, "v0.4.7": 21, "v0.4.6": 20, "v0.4.5": 20, diff --git a/contract-examples/README.md b/contract-examples/README.md index ace8d51138..1e4b2c7cb0 100644 --- a/contract-examples/README.md +++ b/contract-examples/README.md @@ -54,13 +54,52 @@ For more information about precompiles see [subnet-evm precompiles](https://gith Hardhat uses `hardhat.config.js` as the configuration file. You can define tasks, networks, compilers and more in that file. For more information see [here](https://hardhat.org/config/). -In our repository we use a pre-configured file [hardhat.config.ts](https://github.com/ava-labs/avalanche-smart-contract-quickstart/blob/main/hardhat.config.ts). This file configures necessary network information to provide smooth interaction with Avalanche. There are two networks in the hardhat config: `e2e` and `local`. `e2e` network is used for e2e tests and should not be changed. `local` network is used by tasks and for local deployments. There are also some pre-defined private keys for these networks. Each chain in subnets has their own RPC URL. Subnet EVM's RPC URL is in form of: `"http://{ip}:{port}/ext/bc/{chainID}/rpc`. When you create your own subnet and Subnet EVM chain `{chainID}` will be different. You can change `local` network RPC url with the `local_rpc.json`. There is an example file named with `local_rpc_example.json`. You can copy & rename this file to customize the url: +In Subnet-EVM, we provide a pre-configured file [hardhat.config.ts](https://github.com/ava-labs/avalanche-smart-contract-quickstart/blob/main/hardhat.config.ts). +The HardHat config file includes a single network configuration: `local`. `local` defaults to using the following values for the RPC URL and the Chain ID: + +``` +var local_rpc_uri = process.env.RPC_URI || "http://127.0.0.1:9650/ext/bc/C/rpc" +var local_chain_id = process.env.CHAIN_ID || 99999 ``` -cp local_rpc.example.json local_rpc.json + +You can use this network configuration by providing the environment variables and specifying the `--network` flag, as Subnet-EVM does in its testing suite: + +```bash +RPC_URI=http://127.0.0.1:9650/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc CHAIN_ID=77777 npx hardhat test --network local ``` -Do not forget to set correct URL in the `local_rpc.json` file. +Alternatively, you can copy and paste the `local` network configuration to create a new network configuration for your own local testing. For example, you can copy and paste the `local` network configuration to create your own network and fill in the required details: + +```json +{ + networks: { + mynetwork: { + url: "http://127.0.0.1:9650/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc", + chainId: 33333, + accounts: [ + "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", + "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07", + "0x15614556be13730e9e8d6eacc1603143e7b96987429df8726384c2ec4502ef6e", + "0x31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc", + "0x6934bef917e01692b789da754a0eae31a8536eb465e7bff752ea291dad88c675", + "0xe700bdbdbc279b808b1ec45f8c2370e4616d3a02c336e68d85d4668e08f53cff", + "0xbbc2865b76ba28016bc2255c7504d000e046ae01934b04c694592a6276988630", + "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60", + "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c", + "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a" + ], + pollingInterval: "1s" + }, + } +} +``` + +By creating your own network configuration in the HardHat config, you can run HardHat commands directly on your subnet: + +```bash +npx hardhat accounts --network mynetwork +``` ## Hardhat Tasks @@ -68,7 +107,11 @@ You can define custom hardhat tasks in [tasks.ts](https://github.com/ava-labs/av ## Tests -Tests are written for a local network which runs a Subnet-EVM chain. E.g `npx hardhat test --network local`. Subnet-EVM must activate required precompiles with following genesis: +Tests are written for a local network which runs a Subnet-EVM Blockchain. + +E.g `RPC_URI=http://127.0.0.1:9650/ext/bc/28N1Tv5CZziQ3FKCaXmo8xtxoFtuoVA6NvZykAT5MtGjF4JkGs/rpc CHAIN_ID=77777 npx hardhat test --network local`. + +Subnet-EVM must activate any precompiles used in the test in the genesis: ```json { diff --git a/contract-examples/hardhat.config.ts b/contract-examples/hardhat.config.ts index ab17286bcf..1e4c995b7d 100644 --- a/contract-examples/hardhat.config.ts +++ b/contract-examples/hardhat.config.ts @@ -1,19 +1,10 @@ import "@nomiclabs/hardhat-waffle" import "./tasks.ts" -import { existsSync } from "fs" -// Import the dynamic rpc url if the file exists -let testRpc = "" -if (existsSync("./dynamic_rpc.json")) { - const importedRpc = require("./dynamic_rpc.json") - testRpc = importedRpc.rpc -} - -var localConf -if (existsSync("./local_rpc.json")) { - const importedRpc = require("./local_rpc.json") - localConf = importedRpc -} +// HardHat users must populate these environment variables in order to connect to their subnet-evm instance +// Since the blockchainID is not known in advance, there's no good default to use and we use the C-Chain here. +var local_rpc_uri = process.env.RPC_URI || "http://127.0.0.1:9650/ext/bc/C/rpc" +var local_chain_id = parseInt(process.env.CHAIN_ID,10) || 99999 export default { solidity: { @@ -38,26 +29,9 @@ export default { networks: { local: { //"http://{ip}:{port}/ext/bc/{chainID}/rpc - // modify this in the local_rpc.json - url: localConf.rpc, - chainId: localConf.chainId, - accounts: [ - "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", - "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07", - "0x15614556be13730e9e8d6eacc1603143e7b96987429df8726384c2ec4502ef6e", - "0x31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc", - "0x6934bef917e01692b789da754a0eae31a8536eb465e7bff752ea291dad88c675", - "0xe700bdbdbc279b808b1ec45f8c2370e4616d3a02c336e68d85d4668e08f53cff", - "0xbbc2865b76ba28016bc2255c7504d000e046ae01934b04c694592a6276988630", - "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60", - "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c", - "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a" - ] - }, - e2e: { - //"http://{ip}:{port}/ext/bc/{chainID}/rpc - url: testRpc, - chainId: 99999, + // expected to be populated by the environment variables above + url: local_rpc_uri, + chainId: local_chain_id, accounts: [ "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07", @@ -69,7 +43,8 @@ export default { "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60", "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c", "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a" - ] + ], + pollingInterval: "1s" }, } } diff --git a/contract-examples/local_rpc.example.json b/contract-examples/local_rpc.example.json deleted file mode 100644 index bcd2516399..0000000000 --- a/contract-examples/local_rpc.example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rpc": "http://127.0.0.1:9650/ext/bc/dRTfPJh4jEaRZoGkPc7xreeYbDGBrGWRV48WAYVyUgApsmzGo/rpc", - "chainId": 43214 -} diff --git a/contract-examples/test/ExampleDeployerList.ts b/contract-examples/test/contract_deployer_allow_list.ts similarity index 100% rename from contract-examples/test/ExampleDeployerList.ts rename to contract-examples/test/contract_deployer_allow_list.ts diff --git a/contract-examples/test/ERC20NativeMinter.ts b/contract-examples/test/contract_native_minter.ts similarity index 100% rename from contract-examples/test/ERC20NativeMinter.ts rename to contract-examples/test/contract_native_minter.ts diff --git a/contract-examples/test/ExampleFeeManager.ts b/contract-examples/test/fee_manager.ts similarity index 100% rename from contract-examples/test/ExampleFeeManager.ts rename to contract-examples/test/fee_manager.ts diff --git a/contract-examples/test/ExampleRewardManager.ts b/contract-examples/test/reward_manager.ts similarity index 100% rename from contract-examples/test/ExampleRewardManager.ts rename to contract-examples/test/reward_manager.ts diff --git a/contract-examples/test/ExampleTxAllowList.ts b/contract-examples/test/tx_allow_list.ts similarity index 100% rename from contract-examples/test/ExampleTxAllowList.ts rename to contract-examples/test/tx_allow_list.ts diff --git a/core/blockchain.go b/core/blockchain.go index 226e32a96e..50e688e473 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -85,9 +85,13 @@ var ( acceptedBlockGasUsedCounter = metrics.NewRegisteredCounter("chain/block/gas/used/accepted", nil) badBlockCounter = metrics.NewRegisteredCounter("chain/block/bad/count", nil) + txUnindexTimer = metrics.NewRegisteredCounter("chain/txs/unindex", nil) acceptedTxsCounter = metrics.NewRegisteredCounter("chain/txs/accepted", nil) processedTxsCounter = metrics.NewRegisteredCounter("chain/txs/processed", nil) + acceptedLogsCounter = metrics.NewRegisteredCounter("chain/logs/accepted", nil) + processedLogsCounter = metrics.NewRegisteredCounter("chain/logs/processed", nil) + ErrRefuseToCorruptArchiver = errors.New("node has operated with pruning disabled, shutting down to prevent missing tries") errFutureBlockUnsupported = errors.New("future block insertion not supported") @@ -102,7 +106,6 @@ const ( feeConfigCacheLimit = 256 coinbaseConfigCacheLimit = 256 badBlockLimit = 10 - TriesInMemory = 128 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. // @@ -173,6 +176,7 @@ type CacheConfig struct { SkipSnapshotRebuild bool // Whether to skip rebuilding the snapshot in favor of returning an error (only set to true for tests) Preimages bool // Whether to store preimage of trie key to the disk AcceptedCacheSize int // Depth of accepted headers cache and accepted logs cache at the accepted tip + TxLookupLimit uint64 // Number of recent blocks for which to maintain transaction lookup indices } var DefaultCacheConfig = &CacheConfig{ @@ -269,9 +273,8 @@ type BlockChain struct { // during shutdown and in tests. acceptorWg sync.WaitGroup - // [rejournalWg] is used to wait for the trie clean rejournaling to complete. - // This is used during shutdown. - rejournalWg sync.WaitGroup + // [wg] is used to wait for the async blockchain processes to finish on shutdown. + wg sync.WaitGroup // quit channel is used to listen for when the blockchain is shut down to close // async processes. @@ -354,6 +357,13 @@ func NewBlockChain( // Create the state manager bc.stateManager = NewTrieWriter(bc.stateCache.TrieDB(), cacheConfig) + // loadLastState writes indices, so we should start the tx indexer after that. + // Start tx indexer/unindexer here. + if bc.cacheConfig.TxLookupLimit != 0 { + bc.wg.Add(1) + go bc.dispatchTxUnindexer() + } + // Re-generate current block state if it is missing if err := bc.loadLastState(lastAcceptedHash); err != nil { return nil, err @@ -401,9 +411,9 @@ func NewBlockChain( log.Info("Starting to save trie clean cache periodically", "journalDir", bc.cacheConfig.TrieCleanJournal, "freq", bc.cacheConfig.TrieCleanRejournal) triedb := bc.stateCache.TrieDB() - bc.rejournalWg.Add(1) + bc.wg.Add(1) go func() { - defer bc.rejournalWg.Done() + defer bc.wg.Done() triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit) }() } @@ -411,6 +421,72 @@ func NewBlockChain( return bc, nil } +// dispatchTxUnindexer is responsible for the deletion of the +// transaction index. +// Invariant: If TxLookupLimit is 0, it means all tx indices will be preserved. +// Meaning that this function should never be called. +func (bc *BlockChain) dispatchTxUnindexer() { + defer bc.wg.Done() + txLookupLimit := bc.cacheConfig.TxLookupLimit + + // If the user just upgraded to a new version which supports transaction + // index pruning, write the new tail and remove anything older. + if rawdb.ReadTxIndexTail(bc.db) == nil { + rawdb.WriteTxIndexTail(bc.db, 0) + } + + // unindexes transactions depending on user configuration + unindexBlocks := func(tail uint64, head uint64, done chan struct{}) { + start := time.Now() + defer func() { + txUnindexTimer.Inc(time.Since(start).Milliseconds()) + done <- struct{}{} + }() + + // Update the transaction index to the new chain state + if head-txLookupLimit+1 >= tail { + // Unindex a part of stale indices and forward index tail to HEAD-limit + rawdb.UnindexTransactions(bc.db, tail, head-txLookupLimit+1, bc.quit) + } + } + // Any reindexing done, start listening to chain events and moving the index window + var ( + done chan struct{} // Non-nil if background unindexing or reindexing routine is active. + headCh = make(chan ChainEvent, 1) // Buffered to avoid locking up the event feed + ) + sub := bc.SubscribeChainAcceptedEvent(headCh) + if sub == nil { + log.Warn("could not create chain accepted subscription to unindex txs") + return + } + defer sub.Unsubscribe() + + for { + select { + case head := <-headCh: + headNum := head.Block.NumberU64() + if headNum < txLookupLimit { + break + } + + if done == nil { + done = make(chan struct{}) + // Note: tail will not be nil since it is initialized in this function. + tail := rawdb.ReadTxIndexTail(bc.db) + go unindexBlocks(*tail, headNum, done) + } + case <-done: + done = nil + case <-bc.quit: + if done != nil { + log.Info("Waiting background transaction indexer to exit") + <-done + } + return + } + } +} + // writeBlockAcceptedIndices writes any indices that must be persisted for accepted block. // This includes the following: // - transaction lookup indices @@ -532,6 +608,9 @@ func (bc *BlockChain) startAcceptor() { acceptorWorkTimer.Inc(time.Since(start).Milliseconds()) acceptorWorkCount.Inc(1) + // Note: in contrast to most accepted metrics, we increment the accepted log metrics in the acceptor queue because + // the logs are already processed in the acceptor queue. + acceptedLogsCounter.Inc(int64(len(logs))) } } @@ -555,8 +634,8 @@ func (bc *BlockChain) addAcceptorQueue(b *types.Block) { // DrainAcceptorQueue blocks until all items in [acceptorQueue] have been // processed. func (bc *BlockChain) DrainAcceptorQueue() { - bc.acceptorClosingLock.Lock() - defer bc.acceptorClosingLock.Unlock() + bc.acceptorClosingLock.RLock() + defer bc.acceptorClosingLock.RUnlock() if bc.acceptorClosed { return @@ -782,7 +861,8 @@ func (bc *BlockChain) ValidateCanonicalChain() error { // Transactions are only indexed beneath the last accepted block, so we only check // that the transactions have been indexed, if we are checking below the last accepted // block. - if current.NumberU64() <= bc.lastAccepted.NumberU64() { + shouldIndexTxs := bc.cacheConfig.TxLookupLimit == 0 || bc.lastAccepted.NumberU64() < current.NumberU64()+bc.cacheConfig.TxLookupLimit + if current.NumberU64() <= bc.lastAccepted.NumberU64() && shouldIndexTxs { // Ensure that all of the transactions have been stored correctly in the canonical // chain for txIndex, tx := range txs { @@ -840,7 +920,6 @@ func (bc *BlockChain) Stop() { return } - // Wait for accepted feed to process all remaining items log.Info("Closing quit channel") close(bc.quit) // Wait for accepted feed to process all remaining items @@ -868,9 +947,9 @@ func (bc *BlockChain) Stop() { log.Info("Closing scope") bc.scope.Close() - // Waiting for clean trie re-journal to complete - log.Info("Waiting for trie re-journal to complete") - bc.rejournalWg.Wait() + // Waiting for background processes to complete + log.Info("Waiting for background processes to complete") + bc.wg.Wait() log.Info("Blockchain stopped") } @@ -1313,6 +1392,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error { processedBlockGasUsedCounter.Inc(int64(block.GasUsed())) processedTxsCounter.Inc(int64(block.Transactions().Len())) + processedLogsCounter.Inc(int64(len(logs))) blockInsertCount.Inc(1) return nil } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 270eef14df..e4eb86c3d0 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -745,3 +745,142 @@ func TestCanonicalHashMarker(t *testing.T) { } } } + +func TestTransactionIndices(t *testing.T) { + // Configure and generate a sample block chain + require := require.New(t) + var ( + gendb = rawdb.NewMemoryDatabase() + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + funds = big.NewInt(10000000000000) + gspec = &Genesis{ + Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{addr1: {Balance: funds}}, + } + genesis = gspec.MustCommit(gendb) + signer = types.LatestSigner(gspec.Config) + ) + height := uint64(128) + blocks, _, err := GenerateChain(gspec.Config, genesis, dummy.NewFaker(), gendb, int(height), 10, func(i int, block *BlockGen) { + tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + require.NoError(err) + block.AddTx(tx) + }) + require.NoError(err) + + blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), gendb, 10, 10, nil) + require.NoError(err) + + check := func(tail *uint64, chain *BlockChain) { + stored := rawdb.ReadTxIndexTail(chain.db) + require.EqualValues(tail, stored) + + if tail == nil { + return + } + for i := *tail; i <= chain.CurrentBlock().NumberU64(); i++ { + block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i) + if block.Transactions().Len() == 0 { + continue + } + for _, tx := range block.Transactions() { + index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()) + require.NotNilf(index, "Miss transaction indices, number %d hash %s", i, tx.Hash().Hex()) + } + } + + for i := uint64(0); i < *tail; i++ { + block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i) + if block.Transactions().Len() == 0 { + continue + } + for _, tx := range block.Transactions() { + index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()) + require.Nilf(index, "Transaction indices should be deleted, number %d hash %s", i, tx.Hash().Hex()) + } + } + } + + conf := &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieDirtyCommitTarget: 20, + Pruning: true, + CommitInterval: 4096, + SnapshotLimit: 256, + SkipSnapshotRebuild: true, // Ensure the test errors if snapshot initialization fails + AcceptorQueueLimit: 64, + } + + // Init block chain and check all needed indices has been indexed. + chainDB := rawdb.NewMemoryDatabase() + gspec.MustCommit(chainDB) + + chain, err := createBlockChain(chainDB, conf, gspec.Config, common.Hash{}) + require.NoError(err) + + _, err = chain.InsertChain(blocks) + require.NoError(err) + + for _, block := range blocks { + err := chain.Accept(block) + require.NoError(err) + } + chain.DrainAcceptorQueue() + + chain.Stop() + check(nil, chain) // check all indices has been indexed + + lastAcceptedHash := chain.CurrentHeader().Hash() + + // Reconstruct a block chain which only reserves limited tx indices + // 128 blocks were previously indexed. Now we add a new block at each test step. + limit := []uint64{130 /* 129 + 1 reserve all */, 64 /* drop stale */, 32 /* shorten history */} + tails := []uint64{0 /* reserve all */, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */} + for i, l := range limit { + conf.TxLookupLimit = l + + chain, err := createBlockChain(chainDB, conf, gspec.Config, lastAcceptedHash) + require.NoError(err) + + newBlks := blocks2[i : i+1] + _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater. + require.NoError(err) + + err = chain.Accept(newBlks[0]) // Accept the block to trigger indices updater. + require.NoError(err) + + chain.DrainAcceptorQueue() + time.Sleep(50 * time.Millisecond) // Wait for indices initialisation + + chain.Stop() + check(&tails[i], chain) + + lastAcceptedHash = chain.CurrentHeader().Hash() + } +} + +func TestTxLookupBlockChain(t *testing.T) { + cacheConf := &CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieDirtyCommitTarget: 20, + Pruning: true, + CommitInterval: 4096, + SnapshotLimit: 256, + SkipSnapshotRebuild: true, // Ensure the test errors if snapshot initialization fails + AcceptorQueueLimit: 64, // ensure channel doesn't block + TxLookupLimit: 5, + } + createTxLookupBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { + return createBlockChain(db, cacheConf, chainConfig, lastAcceptedHash) + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + tt.testFunc(t, createTxLookupBlockChain) + }) + } +} diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index d7add47ed8..8b4829139c 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -588,3 +588,23 @@ func ReadHeadBlock(db ethdb.Reader) *types.Block { } return ReadBlock(db, headBlockHash, *headBlockNumber) } + +// ReadTxIndexTail retrieves the number of oldest indexed block +// whose transaction indices has been indexed. If the corresponding entry +// is non-existent in database it means the indexing has been finished. +func ReadTxIndexTail(db ethdb.KeyValueReader) *uint64 { + data, _ := db.Get(txIndexTailKey) + if len(data) != 8 { + return nil + } + number := binary.BigEndian.Uint64(data) + return &number +} + +// WriteTxIndexTail stores the number of oldest indexed block +// into database. +func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) { + if err := db.Put(txIndexTailKey, encodeBlockNumber(number)); err != nil { + log.Crit("Failed to store the transaction index tail", "err", err) + } +} diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go new file mode 100644 index 0000000000..cba39b57c6 --- /dev/null +++ b/core/rawdb/chain_iterator.go @@ -0,0 +1,311 @@ +// (c) 2019-2022, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "runtime" + "sync/atomic" + "time" + + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethdb" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +type blockTxHashes struct { + number uint64 + hashes []common.Hash +} + +// iterateTransactions iterates over all transactions in the (canon) block +// number(s) given, and yields the hashes on a channel. If there is a signal +// received from interrupt channel, the iteration will be aborted and result +// channel will be closed. +// Iterates blocks in the range [from, to) +func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool, interrupt chan struct{}) chan *blockTxHashes { + // One thread sequentially reads data from db + type numberRlp struct { + number uint64 + rlp rlp.RawValue + } + if to == from { + return nil + } + threads := to - from + if cpus := runtime.NumCPU(); threads > uint64(cpus) { + threads = uint64(cpus) + } + var ( + rlpCh = make(chan *numberRlp, threads*2) // we send raw rlp over this channel + hashesCh = make(chan *blockTxHashes, threads*2) // send hashes over hashesCh + ) + // lookup runs in one instance + lookup := func() { + n, end := from, to + if reverse { + n, end = to-1, from-1 + } + defer close(rlpCh) + for n != end { + data := ReadCanonicalBodyRLP(db, n) + // Feed the block to the aggregator, or abort on interrupt + select { + case rlpCh <- &numberRlp{n, data}: + case <-interrupt: + return + } + if reverse { + n-- + } else { + n++ + } + } + } + // process runs in parallel + nThreadsAlive := int32(threads) + process := func() { + defer func() { + // Last processor closes the result channel + if atomic.AddInt32(&nThreadsAlive, -1) == 0 { + close(hashesCh) + } + }() + for data := range rlpCh { + var body types.Body + if err := rlp.DecodeBytes(data.rlp, &body); err != nil { + log.Warn("Failed to decode block body", "block", data.number, "error", err) + return + } + var hashes []common.Hash + for _, tx := range body.Transactions { + hashes = append(hashes, tx.Hash()) + } + result := &blockTxHashes{ + hashes: hashes, + number: data.number, + } + // Feed the block to the aggregator, or abort on interrupt + select { + case hashesCh <- result: + case <-interrupt: + return + } + } + } + go lookup() // start the sequential db accessor + for i := 0; i < int(threads); i++ { + go process() + } + return hashesCh +} + +// indexTransactions creates txlookup indices of the specified block range. +// +// This function iterates canonical chain in reverse order, it has one main advantage: +// We can write tx index tail flag periodically even without the whole indexing +// procedure is finished. So that we can resume indexing procedure next time quickly. +// +// There is a passed channel, the whole procedure will be interrupted if any +// signal received. +func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { + // short circuit for invalid range + if from >= to { + return + } + var ( + hashesCh = iterateTransactions(db, from, to, true, interrupt) + batch = db.NewBatch() + start = time.Now() + logged = start.Add(-7 * time.Second) + // Since we iterate in reverse, we expect the first number to come + // in to be [to-1]. Therefore, setting lastNum to means that the + // prqueue gap-evaluation will work correctly + lastNum = to + queue = prque.New(nil) + // for stats reporting + blocks, txs = 0, 0 + ) + for chanDelivery := range hashesCh { + // Push the delivery into the queue and process contiguous ranges. + // Since we iterate in reverse, so lower numbers have lower prio, and + // we can use the number directly as prio marker + queue.Push(chanDelivery, int64(chanDelivery.number)) + for !queue.Empty() { + // If the next available item is gapped, return + if _, priority := queue.Peek(); priority != int64(lastNum-1) { + break + } + // For testing + if hook != nil && !hook(lastNum-1) { + break + } + // Next block available, pop it off and index it + delivery := queue.PopItem().(*blockTxHashes) + lastNum = delivery.number + WriteTxLookupEntries(batch, delivery.number, delivery.hashes) + blocks++ + txs += len(delivery.hashes) + // If enough data was accumulated in memory or we're at the last block, dump to disk + if batch.ValueSize() > ethdb.IdealBatchSize { + WriteTxIndexTail(batch, lastNum) // Also write the tail here + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return + } + batch.Reset() + } + // If we've spent too much time already, notify the user of what we're doing + if time.Since(logged) > 8*time.Second { + log.Info("Indexing transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "total", to-from, "elapsed", common.PrettyDuration(time.Since(start))) + logged = time.Now() + } + } + } + // Flush the new indexing tail and the last committed data. It can also happen + // that the last batch is empty because nothing to index, but the tail has to + // be flushed anyway. + WriteTxIndexTail(batch, lastNum) + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return + } + select { + case <-interrupt: + log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) + default: + log.Info("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) + } +} + +// // IndexTransactions creates txlookup indices of the specified block range. The from +// // is included while to is excluded. +// // +// // This function iterates canonical chain in reverse order, it has one main advantage: +// // We can write tx index tail flag periodically even without the whole indexing +// // procedure is finished. So that we can resume indexing procedure next time quickly. +// // +// // There is a passed channel, the whole procedure will be interrupted if any +// // signal received. +// func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { +// indexTransactions(db, from, to, interrupt, nil) +// } + +// indexTransactionsForTesting is the internal debug version with an additional hook. +func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { + indexTransactions(db, from, to, interrupt, hook) +} + +// unindexTransactions removes txlookup indices of the specified block range. +// +// There is a passed channel, the whole procedure will be interrupted if any +// signal received. +func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { + // short circuit for invalid range + if from >= to { + return + } + var ( + hashesCh = iterateTransactions(db, from, to, false, interrupt) + batch = db.NewBatch() + start = time.Now() + logged = start.Add(-7 * time.Second) + // we expect the first number to come in to be [from]. Therefore, setting + // nextNum to from means that the prqueue gap-evaluation will work correctly + nextNum = from + queue = prque.New(nil) + // for stats reporting + blocks, txs = 0, 0 + ) + // Otherwise spin up the concurrent iterator and unindexer + for delivery := range hashesCh { + // Push the delivery into the queue and process contiguous ranges. + queue.Push(delivery, -int64(delivery.number)) + for !queue.Empty() { + // If the next available item is gapped, return + if _, priority := queue.Peek(); -priority != int64(nextNum) { + break + } + // For testing + if hook != nil && !hook(nextNum) { + break + } + delivery := queue.PopItem().(*blockTxHashes) + nextNum = delivery.number + 1 + DeleteTxLookupEntries(batch, delivery.hashes) + txs += len(delivery.hashes) + blocks++ + + // If enough data was accumulated in memory or we're at the last block, dump to disk + // A batch counts the size of deletion as '1', so we need to flush more + // often than that. + if blocks%1000 == 0 { + WriteTxIndexTail(batch, nextNum) + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return + } + batch.Reset() + } + // If we've spent too much time already, notify the user of what we're doing + if time.Since(logged) > 8*time.Second { + log.Info("Unindexing transactions", "blocks", blocks, "txs", txs, "total", to-from, "elapsed", common.PrettyDuration(time.Since(start))) + logged = time.Now() + } + } + } + // Flush the new indexing tail and the last committed data. It can also happen + // that the last batch is empty because nothing to unindex, but the tail has to + // be flushed anyway. + WriteTxIndexTail(batch, nextNum) + if err := batch.Write(); err != nil { + log.Crit("Failed writing batch to db", "error", err) + return + } + select { + case <-interrupt: + log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) + default: + log.Info("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) + } +} + +// UnindexTransactions removes txlookup indices of the specified block range. +// The from is included while to is excluded. +// +// There is a passed channel, the whole procedure will be interrupted if any +// signal received. +func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { + unindexTransactions(db, from, to, interrupt, nil) +} + +// unindexTransactionsForTesting is the internal debug version with an additional hook. +func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { + unindexTransactions(db, from, to, interrupt, hook) +} diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go new file mode 100644 index 0000000000..0873dd3667 --- /dev/null +++ b/core/rawdb/chain_iterator_test.go @@ -0,0 +1,218 @@ +// (c) 2019-2022, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "math/big" + "reflect" + "sort" + "sync" + "testing" + + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ethereum/go-ethereum/common" +) + +func TestChainIterator(t *testing.T) { + // Construct test chain db + chainDb := NewMemoryDatabase() + + var block *types.Block + var txs []*types.Transaction + to := common.BytesToAddress([]byte{0x11}) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) // Empty genesis block + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + for i := uint64(1); i <= 10; i++ { + var tx *types.Transaction + if i%2 == 0 { + tx = types.NewTx(&types.LegacyTx{ + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) + } else { + tx = types.NewTx(&types.AccessListTx{ + ChainID: big.NewInt(1337), + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) + } + txs = append(txs, tx) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + } + + cases := []struct { + from, to uint64 + reverse bool + expect []int + }{ + {0, 11, true, []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}, + {0, 0, true, nil}, + {0, 5, true, []int{4, 3, 2, 1, 0}}, + {10, 11, true, []int{10}}, + {0, 11, false, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + {0, 0, false, nil}, + {10, 11, false, []int{10}}, + } + for i, c := range cases { + var numbers []int + hashCh := iterateTransactions(chainDb, c.from, c.to, c.reverse, nil) + if hashCh != nil { + for h := range hashCh { + numbers = append(numbers, int(h.number)) + if len(h.hashes) > 0 { + if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp { + t.Fatalf("block %d: hash wrong, got %x exp %x", h.number, got, exp) + } + } + } + } + if !c.reverse { + sort.Ints(numbers) + } else { + sort.Sort(sort.Reverse(sort.IntSlice(numbers))) + } + if !reflect.DeepEqual(numbers, c.expect) { + t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers) + } + } +} + +func TestIndexTransactions(t *testing.T) { + // Construct test chain db + chainDb := NewMemoryDatabase() + + var block *types.Block + var txs []*types.Transaction + to := common.BytesToAddress([]byte{0x11}) + + // Write empty genesis block + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newHasher()) + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + + for i := uint64(1); i <= 10; i++ { + var tx *types.Transaction + if i%2 == 0 { + tx = types.NewTx(&types.LegacyTx{ + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) + } else { + tx = types.NewTx(&types.AccessListTx{ + ChainID: big.NewInt(1337), + Nonce: i, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &to, + Value: big.NewInt(111), + Data: []byte{0x11, 0x11, 0x11}, + }) + } + txs = append(txs, tx) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher()) + WriteBlock(chainDb, block) + WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) + } + // verify checks whether the tx indices in the range [from, to) + // is expected. + verify := func(from, to int, exist bool, tail uint64) { + for i := from; i < to; i++ { + if i == 0 { + continue + } + number := ReadTxLookupEntry(chainDb, txs[i-1].Hash()) + if exist && number == nil { + t.Fatalf("Transaction index %d missing", i) + } + if !exist && number != nil { + t.Fatalf("Transaction index %d is not deleted", i) + } + } + number := ReadTxIndexTail(chainDb) + if number == nil || *number != tail { + t.Fatalf("Transaction tail mismatch") + } + } + indexTransactionsForTesting(chainDb, 5, 11, nil, nil) + verify(5, 11, true, 5) + verify(0, 5, false, 5) + + indexTransactionsForTesting(chainDb, 0, 5, nil, nil) + verify(0, 11, true, 0) + + UnindexTransactions(chainDb, 0, 5, nil) + verify(5, 11, true, 5) + verify(0, 5, false, 5) + + UnindexTransactions(chainDb, 5, 11, nil) + verify(0, 11, false, 11) + + // Testing corner cases + signal := make(chan struct{}) + var once sync.Once + indexTransactionsForTesting(chainDb, 5, 11, signal, func(n uint64) bool { + if n <= 8 { + once.Do(func() { + close(signal) + }) + return false + } + return true + }) + verify(9, 11, true, 9) + verify(0, 9, false, 9) + indexTransactionsForTesting(chainDb, 0, 9, nil, nil) + + signal = make(chan struct{}) + var once2 sync.Once + unindexTransactionsForTesting(chainDb, 0, 11, signal, func(n uint64) bool { + if n >= 8 { + once2.Do(func() { + close(signal) + }) + return false + } + return true + }) + verify(8, 11, true, 8) + verify(0, 8, false, 8) +} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 51c3da8065..dc4dd85688 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -208,7 +208,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { for _, meta := range [][]byte{ databaseVersionKey, headHeaderKey, headBlockKey, snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey, - uncleanShutdownKey, syncRootKey, + uncleanShutdownKey, syncRootKey, txIndexTailKey, } { if bytes.Equal(key, meta) { metadata.Add(size) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 4eb3382a84..bb0956abdf 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -56,6 +56,9 @@ var ( // snapshotGeneratorKey tracks the snapshot generation marker across restarts. snapshotGeneratorKey = []byte("SnapshotGenerator") + // txIndexTailKey tracks the oldest block whose transactions have been indexed. + txIndexTailKey = []byte("TransactionIndexTail") + // uncleanShutdownKey tracks the list of local crashes uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index aea3fed5ed..b6057a9211 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -599,6 +599,7 @@ func diffToDisk(bottom *diffLayer) (*diskLayer, bool, error) { // Mark the original base as stale as we're going to create a new wrapper base.lock.Lock() if base.stale { + base.lock.Unlock() return nil, false, ErrStaleParentLayer // we've committed into the same base from two children, boo } base.stale = true diff --git a/eth/backend.go b/eth/backend.go index 7e9aaebd84..958460a359 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -215,6 +215,7 @@ func New( SkipSnapshotRebuild: config.SkipSnapshotRebuild, Preimages: config.Preimages, AcceptedCacheSize: config.AcceptedCacheSize, + TxLookupLimit: config.TxLookupLimit, } ) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index c1be8c0ead..98a16165a7 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -149,4 +149,10 @@ type Config struct { // their node before the network upgrade and their node accepts blocks that have // identical state with the pre-upgrade ruleset. SkipUpgradeCheck bool + + // TxLookupLimit is the maximum number of blocks from head whose tx indices + // are reserved: + // * 0: means no limit + // * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes + TxLookupLimit uint64 } diff --git a/eth/filters/bench_test.go b/eth/filters/bench_test.go index e9a55ae102..7ba3381f2f 100644 --- a/eth/filters/bench_test.go +++ b/eth/filters/bench_test.go @@ -73,7 +73,7 @@ const benchFilterCnt = 2000 func benchmarkBloomBits(b *testing.B, sectionSize uint64) { b.Skip("test disabled: this tests presume (and modify) an existing datadir.") - benchDataDir := b.TempDir() + "/coreth/chaindata" + benchDataDir := b.TempDir() + "/subnet-evm/chaindata" b.Log("Running bloombits benchmark section size:", sectionSize) db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) @@ -173,7 +173,7 @@ func clearBloomBits(db ethdb.Database) { func BenchmarkNoBloomBits(b *testing.B) { b.Skip("test disabled: this tests presume (and modify) an existing datadir.") - benchDataDir := b.TempDir() + "/coreth/chaindata" + benchDataDir := b.TempDir() + "/subnet-evm/chaindata" b.Log("Running benchmark without bloombits") db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false) if err != nil { diff --git a/go.mod b/go.mod index 858efb956e..72b82932ec 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,16 @@ go 1.18 require ( github.com/VictoriaMetrics/fastcache v1.10.0 - github.com/ava-labs/avalanche-network-runner-sdk v0.3.0 - github.com/ava-labs/avalanchego v1.9.5 + github.com/ava-labs/avalanchego v1.9.8 github.com/cespare/cp v0.1.0 - github.com/creack/pty v1.1.18 github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set v1.8.0 github.com/ethereum/go-ethereum v1.10.26 - github.com/fatih/color v1.13.0 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 github.com/go-cmd/cmd v1.4.1 - github.com/google/uuid v1.2.0 + github.com/google/uuid v1.3.0 github.com/gorilla/rpc v1.2.0 github.com/gorilla/websocket v1.4.2 github.com/hashicorp/go-bexpr v0.1.10 @@ -26,11 +23,11 @@ require ( github.com/mattn/go-colorable v0.1.12 github.com/mattn/go-isatty v0.0.14 github.com/olekukonko/tablewriter v0.0.5 - github.com/onsi/ginkgo/v2 v2.4.0 - github.com/onsi/gomega v1.24.0 - github.com/prometheus/client_golang v1.13.0 - github.com/prometheus/client_model v0.2.0 - github.com/rjeczalik/notify v0.9.2 + github.com/onsi/ginkgo/v2 v2.6.1 + github.com/onsi/gomega v1.24.2 + github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_model v0.3.0 + github.com/rjeczalik/notify v0.9.3 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cast v1.5.0 github.com/spf13/pflag v1.0.5 @@ -42,23 +39,25 @@ require ( github.com/urfave/cli/v2 v2.10.2 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d golang.org/x/sync v0.1.0 - golang.org/x/sys v0.1.0 - golang.org/x/text v0.4.0 + golang.org/x/sys v0.3.0 + golang.org/x/text v0.5.0 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac gopkg.in/urfave/cli.v1 v1.20.0 - gopkg.in/yaml.v2 v2.4.0 - sigs.k8s.io/yaml v1.3.0 ) require ( + github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200627015759-01fd2de07837 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -67,10 +66,12 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect github.com/hashicorp/go-hclog v1.2.2 // indirect github.com/hashicorp/go-plugin v1.4.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -93,6 +94,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect + github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.37.0 // indirect @@ -100,6 +102,7 @@ require ( github.com/prometheus/tsdb v0.10.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.3.0 // indirect @@ -118,16 +121,17 @@ require ( go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.23.0 // indirect + go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/term v0.3.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.51.0-dev // indirect + google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect + google.golang.org/grpc v1.53.0-dev // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 718a7e5494..0b4e002aee 100644 --- a/go.sum +++ b/go.sum @@ -44,9 +44,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= 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/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -58,10 +61,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/ava-labs/avalanche-network-runner-sdk v0.3.0 h1:TVi9JEdKNU/RevYZ9PyW4pULbEdS+KQDA9Ki2DUvuAs= -github.com/ava-labs/avalanche-network-runner-sdk v0.3.0/go.mod h1:SgKJvtqvgo/Bl/c8fxEHCLaSxEbzimYfBopcfrajxQk= -github.com/ava-labs/avalanchego v1.9.5 h1:0uykbcKFocUL7U7SO/PGrXSMLX9RSt8xo5rj84bI3YY= -github.com/ava-labs/avalanchego v1.9.5/go.mod h1:1f/z4CBcz/VhNlOTj607dQj5ZQQaZQO/RO8sEa0EgvA= +github.com/ava-labs/avalanchego v1.9.8 h1:5SHKqkWpBn9Pqxg2qpzDZ7FQqYFapzaCZwArapBdqAA= +github.com/ava-labs/avalanchego v1.9.8/go.mod h1:t9+R55TgRJxYCekRf/EicIjHBeeEQT04TQxpaF98+yM= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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= @@ -69,10 +70,30 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/btcsuite/btcd v0.23.1 h1:IB8cVQcC2X5mHbnfirLG5IZnkWYNTPlLZVrxUYSotbE= -github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +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/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -97,19 +118,23 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 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/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= 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/v3 v3.0.0-20200627015759-01fd2de07837 h1:g2cyFTu5FKWhCo7L4hVJ797Q506B4EywA7L9I6OebgA= +github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200627015759-01fd2de07837/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= 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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= @@ -224,6 +249,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -255,8 +282,8 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -273,8 +300,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0 h1:kr3j8iIMR4ywO/O0rvksXaJvauGGCMg2zAZIiNZ9uIQ= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0/go.mod h1:ummNFgdgLhhX7aIiy35vVmQNS0rWXknfPE0qe6fmFXg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 h1:1JYBfzqrWPcCclBwxFCPAou9n+q86mfnu7NAeHfte7A= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0/go.mod h1:YDZoGHuwE+ov0c8smSH49WLF3F2LaWnYYuDVd+EWrc0= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= @@ -316,8 +343,11 @@ github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mq github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -333,6 +363,7 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= @@ -403,19 +434,23 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 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/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= @@ -430,6 +465,8 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQm github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= +github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -443,13 +480,14 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 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/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= @@ -467,8 +505,8 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= +github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= +github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= @@ -487,6 +525,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -521,6 +561,7 @@ github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295 h1:rVKS9JjtqE4/PscoIsP46sRnJhfq8YFbjlk0fUJTRnY= github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 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/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -578,8 +619,9 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -632,6 +674,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -661,6 +704,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -674,8 +718,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -739,10 +783,12 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -769,13 +815,13 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -785,8 +831,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -927,8 +973,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -949,8 +995,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.51.0-dev h1:JIZpGUpbGAukP4rGiKJ/AnpK9BqMYV6Rdx94XWZckHY= -google.golang.org/grpc v1.51.0-dev/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.53.0-dev h1:Bi96+XIrXJLXPJUff19tRXb7mIijir7agn12zNMaPAg= +google.golang.org/grpc v1.53.0-dev/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= 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= @@ -1006,5 +1052,3 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/params/precompile_config_test.go b/params/precompile_config_test.go index bb77d912e1..1ced4ee9a4 100644 --- a/params/precompile_config_test.go +++ b/params/precompile_config_test.go @@ -64,6 +64,26 @@ func TestVerifyWithChainConfig(t *testing.T) { assert.ErrorContains(t, err, "disable should be [true]") } +func TestVerifyWithChainConfigAtNilTimestamp(t *testing.T) { + admins := []common.Address{{1}} + baseConfig := *SubnetEVMDefaultChainConfig + config := &baseConfig + config.PrecompileUpgrades = []PrecompileUpgrade{ + // this does NOT enable the precompile, so it should be upgradeable. + {Config: txallowlist.NewConfig(nil, nil, nil)}, + } + require.False(t, config.IsPrecompileEnabled(txallowlist.ContractAddress, common.Big0)) // check the precompile is not enabled. + config.PrecompileUpgrades = []PrecompileUpgrade{ + { + // enable TxAllowList at timestamp 5 + Config: txallowlist.NewConfig(big.NewInt(5), admins, nil), + }, + } + + // check this config is valid + require.NoError(t, config.Verify()) +} + func TestVerifyPrecompileUpgrades(t *testing.T) { admins := []common.Address{{1}} tests := []struct { @@ -103,9 +123,11 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { upgrades: []PrecompileUpgrade{ { Config: feemanager.NewConfig(big.NewInt(3), admins, nil, - &commontype.FeeConfig{ - GasLimit: big.NewInt(-1), - }), + func() *commontype.FeeConfig { + feeConfig := DefaultFeeConfig + feeConfig.GasLimit = big.NewInt(-1) + return &feeConfig + }()), }, }, expectedError: "gasLimit = -1 cannot be less than or equal to 0", @@ -115,9 +137,11 @@ func TestVerifyPrecompileUpgrades(t *testing.T) { upgrades: []PrecompileUpgrade{ { Config: feemanager.NewConfig(big.NewInt(3), admins, nil, - &commontype.FeeConfig{ - GasLimit: big.NewInt(0), - }), + func() *commontype.FeeConfig { + feeConfig := DefaultFeeConfig + feeConfig.GasLimit = common.Big0 + return &feeConfig + }()), }, }, expectedError: "gasLimit = 0 cannot be less than or equal to 0", @@ -194,9 +218,11 @@ func TestVerifyPrecompiles(t *testing.T) { name: "invalid initial fee manager config", precompiles: ChainConfigPrecompiles{ feemanager.ConfigKey: feemanager.NewConfig(big.NewInt(3), admins, nil, - &commontype.FeeConfig{ - GasLimit: big.NewInt(-1), - }), + func() *commontype.FeeConfig { + feeConfig := DefaultFeeConfig + feeConfig.GasLimit = big.NewInt(-1) + return &feeConfig + }()), }, expectedError: "gasLimit = -1 cannot be less than or equal to 0", }, diff --git a/peer/client.go b/peer/client.go index 31fe2175a9..6a002d0e38 100644 --- a/peer/client.go +++ b/peer/client.go @@ -19,15 +19,19 @@ var ( // NetworkClient defines ability to send request / response through the Network type NetworkClient interface { - // RequestAny synchronously sends request to a randomly chosen peer with a + // SendAppRequestAny synchronously sends request to an arbitrary peer with a // node version greater than or equal to minVersion. // Returns response bytes, the ID of the chosen peer, and ErrRequestFailed if // the request should be retried. - RequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) + SendAppRequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) - // Request synchronously sends request to the selected nodeID + // SendAppRequest synchronously sends request to the selected nodeID // Returns response bytes, and ErrRequestFailed if the request should be retried. - Request(nodeID ids.NodeID, request []byte) ([]byte, error) + SendAppRequest(nodeID ids.NodeID, request []byte) ([]byte, error) + + // SendCrossChainRequest sends a request to a specific blockchain running on this node. + // Returns response bytes, and ErrRequestFailed if the request failed. + SendCrossChainRequest(chainID ids.ID, request []byte) ([]byte, error) // Gossip sends given gossip message to peers Gossip(gossip []byte) error @@ -51,13 +55,13 @@ func NewNetworkClient(network Network) NetworkClient { } } -// RequestAny synchronously sends request to a randomly chosen peer with a +// SendAppRequestAny synchronously sends request to an arbitrary peer with a // node version greater than or equal to minVersion. // Returns response bytes, the ID of the chosen peer, and ErrRequestFailed if // the request should be retried. -func (c *client) RequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) { +func (c *client) SendAppRequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) { waitingHandler := newWaitingResponseHandler() - nodeID, err := c.network.RequestAny(minVersion, request, waitingHandler) + nodeID, err := c.network.SendAppRequestAny(minVersion, request, waitingHandler) if err != nil { return nil, nodeID, err } @@ -68,11 +72,25 @@ func (c *client) RequestAny(minVersion *version.Application, request []byte) ([] return response, nodeID, nil } -// Request synchronously sends request to the specified nodeID +// SendAppRequest synchronously sends request to the specified nodeID +// Returns response bytes and ErrRequestFailed if the request should be retried. +func (c *client) SendAppRequest(nodeID ids.NodeID, request []byte) ([]byte, error) { + waitingHandler := newWaitingResponseHandler() + if err := c.network.SendAppRequest(nodeID, request, waitingHandler); err != nil { + return nil, err + } + response := <-waitingHandler.responseChan + if waitingHandler.failed { + return nil, ErrRequestFailed + } + return response, nil +} + +// SendCrossChainRequest synchronously sends request to the specified chainID // Returns response bytes and ErrRequestFailed if the request should be retried. -func (c *client) Request(nodeID ids.NodeID, request []byte) ([]byte, error) { +func (c *client) SendCrossChainRequest(chainID ids.ID, request []byte) ([]byte, error) { waitingHandler := newWaitingResponseHandler() - if err := c.network.Request(nodeID, request, waitingHandler); err != nil { + if err := c.network.SendCrossChainRequest(chainID, request, waitingHandler); err != nil { return nil, err } response := <-waitingHandler.responseChan diff --git a/peer/network.go b/peer/network.go index 377235dad3..6d0ebe5990 100644 --- a/peer/network.go +++ b/peer/network.go @@ -30,6 +30,7 @@ const minRequestHandlingDuration = 100 * time.Millisecond var ( errAcquiringSemaphore = errors.New("error acquiring semaphore") + errExpiredRequest = errors.New("expired request") _ Network = &network{} _ validators.Connector = &network{} _ common.AppHandler = &network{} @@ -39,18 +40,21 @@ type Network interface { validators.Connector common.AppHandler - // RequestAny synchronously sends request to a randomly chosen peer with a + // SendAppRequestAny synchronously sends request to an arbitrary peer with a // node version greater than or equal to minVersion. // Returns the ID of the chosen peer, and an error if the request could not // be sent to a peer with the desired [minVersion]. - RequestAny(minVersion *version.Application, message []byte, handler message.ResponseHandler) (ids.NodeID, error) + SendAppRequestAny(minVersion *version.Application, message []byte, handler message.ResponseHandler) (ids.NodeID, error) - // Request sends message to given nodeID, notifying handler when there's a response or timeout - Request(nodeID ids.NodeID, message []byte, handler message.ResponseHandler) error + // SendAppRequest sends message to given nodeID, notifying handler when there's a response or timeout + SendAppRequest(nodeID ids.NodeID, message []byte, handler message.ResponseHandler) error // Gossip sends given gossip message to peers Gossip(gossip []byte) error + // SendCrossChainRequest sends a message to given chainID notifying handler when there's a response or timeout + SendCrossChainRequest(chainID ids.ID, message []byte, handler message.ResponseHandler) error + // Shutdown stops all peer channel listeners and marks the node to have stopped // n.Start() can be called again but the peers will have to be reconnected // by calling OnPeerConnected for each peer @@ -62,6 +66,9 @@ type Network interface { // SetRequestHandler sets the provided request handler as the request handler SetRequestHandler(handler message.RequestHandler) + // SetCrossChainHandler sets the provided cross chain request handler as the cross chain request handler + SetCrossChainRequestHandler(handler message.CrossChainRequestHandler) + // Size returns the size of the network in number of connected peers Size() uint32 @@ -77,74 +84,82 @@ type network struct { self ids.NodeID // NodeID of this node requestIDGen uint32 // requestID counter used to track outbound requests outstandingRequestHandlers map[uint32]message.ResponseHandler // maps avalanchego requestID => message.ResponseHandler - activeRequests *semaphore.Weighted // controls maximum number of active outbound requests + activeAppRequests *semaphore.Weighted // controls maximum number of active outbound requests + activeCrossChainRequests *semaphore.Weighted // controls maximum number of active outbound cross chain requests appSender common.AppSender // avalanchego AppSender for sending messages codec codec.Manager // Codec used for parsing messages - requestHandler message.RequestHandler // maps request type => handler + crossChainCodec codec.Manager // Codec used for parsing cross chain messages + appRequestHandler message.RequestHandler // maps request type => handler + crossChainRequestHandler message.CrossChainRequestHandler // maps cross chain request type => handler gossipHandler message.GossipHandler // maps gossip type => handler peers *peerTracker // tracking of peers & bandwidth - stats stats.RequestHandlerStats // Provide request handler metrics + appStats stats.RequestHandlerStats // Provide request handler metrics + crossChainStats stats.RequestHandlerStats // Provide cross chain request handler metrics } -func NewNetwork(appSender common.AppSender, codec codec.Manager, self ids.NodeID, maxActiveRequests int64) Network { +func NewNetwork(appSender common.AppSender, codec codec.Manager, crossChainCodec codec.Manager, self ids.NodeID, maxActiveAppRequests int64, maxActiveCrossChainRequests int64) Network { return &network{ appSender: appSender, codec: codec, + crossChainCodec: crossChainCodec, self: self, outstandingRequestHandlers: make(map[uint32]message.ResponseHandler), - activeRequests: semaphore.NewWeighted(maxActiveRequests), + activeAppRequests: semaphore.NewWeighted(maxActiveAppRequests), + activeCrossChainRequests: semaphore.NewWeighted(maxActiveCrossChainRequests), gossipHandler: message.NoopMempoolGossipHandler{}, - requestHandler: message.NoopRequestHandler{}, + appRequestHandler: message.NoopRequestHandler{}, + crossChainRequestHandler: message.NoopCrossChainRequestHandler{}, peers: NewPeerTracker(), - stats: stats.NewRequestHandlerStats(), + appStats: stats.NewRequestHandlerStats(), + crossChainStats: stats.NewCrossChainRequestHandlerStats(), } } -// RequestAny synchronously sends request to a randomly chosen peer with a +// SendAppRequestAny synchronously sends request to an arbitrary peer with a // node version greater than or equal to minVersion. If minVersion is nil, // the request will be sent to any peer regardless of their version. // Returns the ID of the chosen peer, and an error if the request could not // be sent to a peer with the desired [minVersion]. -func (n *network) RequestAny(minVersion *version.Application, request []byte, handler message.ResponseHandler) (ids.NodeID, error) { - // Take a slot from total [activeRequests] and block until a slot becomes available. - if err := n.activeRequests.Acquire(context.Background(), 1); err != nil { +func (n *network) SendAppRequestAny(minVersion *version.Application, request []byte, handler message.ResponseHandler) (ids.NodeID, error) { + // Take a slot from total [activeAppRequests] and block until a slot becomes available. + if err := n.activeAppRequests.Acquire(context.Background(), 1); err != nil { return ids.EmptyNodeID, errAcquiringSemaphore } n.lock.Lock() defer n.lock.Unlock() if nodeID, ok := n.peers.GetAnyPeer(minVersion); ok { - return nodeID, n.request(nodeID, request, handler) + return nodeID, n.sendAppRequest(nodeID, request, handler) } - n.activeRequests.Release(1) + n.activeAppRequests.Release(1) return ids.EmptyNodeID, fmt.Errorf("no peers found matching version %s out of %d peers", minVersion, n.peers.Size()) } -// Request sends request message bytes to specified nodeID, notifying the responseHandler on response or failure -func (n *network) Request(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error { +// SendAppRequest sends request message bytes to specified nodeID, notifying the responseHandler on response or failure +func (n *network) SendAppRequest(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error { if nodeID == ids.EmptyNodeID { return fmt.Errorf("cannot send request to empty nodeID, nodeID=%s, requestLen=%d", nodeID, len(request)) } - // Take a slot from total [activeRequests] and block until a slot becomes available. - if err := n.activeRequests.Acquire(context.Background(), 1); err != nil { + // Take a slot from total [activeAppRequests] and block until a slot becomes available. + if err := n.activeAppRequests.Acquire(context.Background(), 1); err != nil { return errAcquiringSemaphore } n.lock.Lock() defer n.lock.Unlock() - return n.request(nodeID, request, responseHandler) + return n.sendAppRequest(nodeID, request, responseHandler) } -// request sends request message bytes to specified nodeID and adds [responseHandler] to [outstandingRequestHandlers] +// sendAppRequest sends request message bytes to specified nodeID and adds [responseHandler] to [outstandingRequestHandlers] // so that it can be invoked when the network receives either a response or failure message. // Assumes [nodeID] is never [self] since we guarantee [self] will not be added to the [peers] map. // Releases active requests semaphore if there was an error in sending the request // Returns an error if [appSender] is unable to make the request. // Assumes write lock is held -func (n *network) request(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error { +func (n *network) sendAppRequest(nodeID ids.NodeID, request []byte, responseHandler message.ResponseHandler) error { log.Debug("sending request to peer", "nodeID", nodeID, "requestLen", len(request)) n.peers.TrackPeer(nodeID) @@ -157,12 +172,10 @@ func (n *network) request(nodeID ids.NodeID, request []byte, responseHandler mes nodeIDs := set.NewSet[ids.NodeID](1) nodeIDs.Add(nodeID) - // send app request to the peer - // on failure: release the activeRequests slot, mark message as processed and return fatal error // Send app request to [nodeID]. - // On failure, release the slot from active requests and [outstandingRequestHandlers]. + // On failure, release the slot from [activeAppRequests] and delete request from [outstandingRequestHandlers] if err := n.appSender.SendAppRequest(context.TODO(), nodeIDs, requestID, request); err != nil { - n.activeRequests.Release(1) + n.activeAppRequests.Release(1) delete(n.outstandingRequestHandlers, requestID) return err } @@ -171,16 +184,116 @@ func (n *network) request(nodeID ids.NodeID, request []byte, responseHandler mes return nil } -func (n *network) CrossChainAppRequest(_ context.Context, requestingChainID ids.ID, requestID uint32, deadline time.Time, request []byte) error { +// SendCrossChainRequest sends request message bytes to specified chainID and adds [handler] to [outstandingRequestHandlers] +// so that it can be invoked when the network receives either a response or failure message. +// Returns an error if [appSender] is unable to make the request. +func (n *network) SendCrossChainRequest(chainID ids.ID, request []byte, handler message.ResponseHandler) error { + // Take a slot from total [activeCrossChainRequests] and block until a slot becomes available. + if err := n.activeCrossChainRequests.Acquire(context.Background(), 1); err != nil { + return errAcquiringSemaphore + } + + n.lock.Lock() + defer n.lock.Unlock() + + // generate requestID + requestID := n.requestIDGen + n.requestIDGen++ + + n.outstandingRequestHandlers[requestID] = handler + + // Send cross chain request to [chainID]. + // On failure, release the slot from [activeCrossChainRequests] and delete request from [outstandingRequestHandlers]. + if err := n.appSender.SendCrossChainAppRequest(context.TODO(), chainID, requestID, request); err != nil { + n.activeCrossChainRequests.Release(1) + delete(n.outstandingRequestHandlers, requestID) + return err + } + + log.Debug("sent request message to chain", "chainID", chainID, "crossChainRequestID", requestID) return nil } -func (n *network) CrossChainAppRequestFailed(_ context.Context, respondingChainID ids.ID, requestID uint32) error { - return nil +// CrossChainAppRequest notifies the VM when another chain in the network requests for data. +// Send a CrossChainAppResponse to [chainID] in response to a valid message using the same +// [requestID] before the deadline. +func (n *network) CrossChainAppRequest(ctx context.Context, requestingChainID ids.ID, requestID uint32, deadline time.Time, request []byte) error { + log.Debug("received CrossChainAppRequest from chain", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request)) + + var req message.CrossChainRequest + if _, err := n.crossChainCodec.Unmarshal(request, &req); err != nil { + log.Debug("failed to unmarshal CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "requestLen", len(request), "err", err) + return nil + } + + bufferedDeadline, err := calculateTimeUntilDeadline(deadline, n.crossChainStats) + if err != nil { + log.Debug("deadline to process CrossChainAppRequest has expired, skipping", "requestingChainID", requestingChainID, "requestID", requestID, "err", err) + return nil + } + + log.Debug("processing incoming CrossChainAppRequest", "requestingChainID", requestingChainID, "requestID", requestID, "req", req) + handleCtx, cancel := context.WithDeadline(context.Background(), bufferedDeadline) + defer cancel() + + responseBytes, err := req.Handle(handleCtx, requestingChainID, requestID, n.crossChainRequestHandler) + switch { + case err != nil && err != context.DeadlineExceeded: + return err // Return a fatal error + case responseBytes != nil: + return n.appSender.SendCrossChainAppResponse(ctx, requestingChainID, requestID, responseBytes) // Propagate fatal error + default: + return nil + } } -func (n *network) CrossChainAppResponse(_ context.Context, respondingChainID ids.ID, requestID uint32, response []byte) error { - return nil +// CrossChainAppRequestFailed can be called by the avalanchego -> VM in following cases: +// - respondingChain doesn't exist +// - invalid CrossChainAppResponse from respondingChain +// - invalid CrossChainRequest was sent to respondingChain +// - request times out before a response is provided +// If [requestID] is not known, this function will emit a log and return a nil error. +// If the response handler returns an error it is propagated as a fatal error. +func (n *network) CrossChainAppRequestFailed(ctx context.Context, respondingChainID ids.ID, requestID uint32) error { + n.lock.Lock() + defer n.lock.Unlock() + + log.Debug("received CrossChainAppRequestFailed from chain", "respondingChainID", respondingChainID, "requestID", requestID) + + handler, exists := n.markRequestFulfilled(requestID) + if !exists { + // Should never happen since the engine should be managing outstanding requests + log.Error("received CrossChainAppRequestFailed to unknown request", "respondingChainID", respondingChainID, "requestID", requestID) + return nil + } + + // We must release the slot + n.activeCrossChainRequests.Release(1) + + return handler.OnFailure() +} + +// CrossChainAppResponse is invoked when there is a +// response received from [respondingChainID] regarding a request the VM sent out +// If [requestID] is not known, this function will emit a log and return a nil error. +// If the response handler returns an error it is propagated as a fatal error. +func (n *network) CrossChainAppResponse(ctx context.Context, respondingChainID ids.ID, requestID uint32, response []byte) error { + n.lock.Lock() + defer n.lock.Unlock() + + log.Debug("received CrossChainAppResponse from responding chain", "respondingChainID", respondingChainID, "requestID", requestID) + + handler, exists := n.markRequestFulfilled(requestID) + if !exists { + // Should never happen since the engine should be managing outstanding requests + log.Error("received CrossChainAppResponse to unknown request", "respondingChainID", respondingChainID, "requestID", requestID, "responseLen", len(response)) + return nil + } + + // We must release the slot + n.activeCrossChainRequests.Release(1) + + return handler.OnResponse(response) } // AppRequest is called by avalanchego -> VM when there is an incoming AppRequest from a peer @@ -189,9 +302,6 @@ func (n *network) CrossChainAppResponse(_ context.Context, respondingChainID ids // sends a response back to the sender if length of response returned by the handler is >0 // expects the deadline to not have been passed func (n *network) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error { - n.lock.RLock() - defer n.lock.RUnlock() - log.Debug("received AppRequest from node", "nodeID", nodeID, "requestID", requestID, "requestLen", len(request)) var req message.Request @@ -200,20 +310,9 @@ func (n *network) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID u return nil } - // calculate how much time is left until the deadline - timeTillDeadline := time.Until(deadline) - n.stats.UpdateTimeUntilDeadline(timeTillDeadline) - - // bufferedDeadline is half the time till actual deadline so that the message has a reasonable chance - // of completing its processing and sending the response to the peer. - timeTillDeadline = time.Duration(timeTillDeadline.Nanoseconds() / 2) - bufferedDeadline := time.Now().Add(timeTillDeadline) - - // check if we have enough time to handle this request - if time.Until(bufferedDeadline) < minRequestHandlingDuration { - // Drop the request if we already missed the deadline to respond. - log.Debug("deadline to process AppRequest has expired, skipping", "nodeID", nodeID, "requestID", requestID, "req", req) - n.stats.IncDeadlineDroppedRequest() + bufferedDeadline, err := calculateTimeUntilDeadline(deadline, n.appStats) + if err != nil { + log.Debug("deadline to process AppRequest has expired, skipping", "nodeID", nodeID, "requestID", requestID, "err", err) return nil } @@ -223,7 +322,7 @@ func (n *network) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID u handleCtx, cancel := context.WithDeadline(context.Background(), bufferedDeadline) defer cancel() - responseBytes, err := req.Handle(handleCtx, nodeID, requestID, n.requestHandler) + responseBytes, err := req.Handle(handleCtx, nodeID, requestID, n.appRequestHandler) switch { case err != nil && err != context.DeadlineExceeded: return err // Return a fatal error @@ -244,48 +343,77 @@ func (n *network) AppResponse(_ context.Context, nodeID ids.NodeID, requestID ui log.Debug("received AppResponse from peer", "nodeID", nodeID, "requestID", requestID) - handler, exists := n.getRequestHandler(requestID) + handler, exists := n.markRequestFulfilled(requestID) if !exists { // Should never happen since the engine should be managing outstanding requests - log.Error("received response to unknown request", "nodeID", nodeID, "requestID", requestID, "responseLen", len(response)) + log.Error("received AppResponse to unknown request", "nodeID", nodeID, "requestID", requestID, "responseLen", len(response)) return nil } - return handler.OnResponse(nodeID, requestID, response) + // We must release the slot + n.activeAppRequests.Release(1) + + return handler.OnResponse(response) } // AppRequestFailed can be called by the avalanchego -> VM in following cases: // - node is benched // - failed to send message to [nodeID] due to a network issue -// - timeout +// - request times out before a response is provided // error returned by this function is expected to be treated as fatal by the engine // returns error only when the response handler returns an error func (n *network) AppRequestFailed(_ context.Context, nodeID ids.NodeID, requestID uint32) error { n.lock.Lock() defer n.lock.Unlock() + log.Debug("received AppRequestFailed from peer", "nodeID", nodeID, "requestID", requestID) - handler, exists := n.getRequestHandler(requestID) + handler, exists := n.markRequestFulfilled(requestID) if !exists { // Should never happen since the engine should be managing outstanding requests - log.Error("received request failed to unknown request", "nodeID", nodeID, "requestID", requestID) + log.Error("received AppRequestFailed to unknown request", "nodeID", nodeID, "requestID", requestID) return nil } - return handler.OnFailure(nodeID, requestID) + // We must release the slot + n.activeAppRequests.Release(1) + + return handler.OnFailure() +} + +// calculateTimeUntilDeadline calculates the time until deadline and drops it if we missed he deadline to response. +// This function updates metrics for both app requests and cross chain requests. +// This is called by either [AppRequest] or [CrossChainAppRequest]. +func calculateTimeUntilDeadline(deadline time.Time, stats stats.RequestHandlerStats) (time.Time, error) { + // calculate how much time is left until the deadline + timeTillDeadline := time.Until(deadline) + stats.UpdateTimeUntilDeadline(timeTillDeadline) + + // bufferedDeadline is half the time till actual deadline so that the message has a reasonable chance + // of completing its processing and sending the response to the peer. + bufferedDeadline := time.Now().Add(timeTillDeadline / 2) + + // check if we have enough time to handle this request + if time.Until(bufferedDeadline) < minRequestHandlingDuration { + // Drop the request if we already missed the deadline to respond. + stats.IncDeadlineDroppedRequest() + return time.Time{}, errExpiredRequest + } + + return bufferedDeadline, nil } -// getRequestHandler fetches the handler for [requestID] and marks the request with [requestID] as having been fulfilled. +// markRequestFulfilled fetches the handler for [requestID] and marks the request with [requestID] as having been fulfilled. // This is called by either [AppResponse] or [AppRequestFailed]. -// assumes that the write lock is held. -func (n *network) getRequestHandler(requestID uint32) (message.ResponseHandler, bool) { +// Assumes that the write lock is held. +func (n *network) markRequestFulfilled(requestID uint32) (message.ResponseHandler, bool) { handler, exists := n.outstandingRequestHandlers[requestID] if !exists { return nil, false } - // mark message as processed, release activeRequests slot + // mark message as processed delete(n.outstandingRequestHandlers, requestID) - n.activeRequests.Release(1) + return handler, true } @@ -354,7 +482,14 @@ func (n *network) SetRequestHandler(handler message.RequestHandler) { n.lock.Lock() defer n.lock.Unlock() - n.requestHandler = handler + n.appRequestHandler = handler +} + +func (n *network) SetCrossChainRequestHandler(handler message.CrossChainRequestHandler) { + n.lock.Lock() + defer n.lock.Unlock() + + n.crossChainRequestHandler = handler } func (n *network) Size() uint32 { diff --git a/peer/network_test.go b/peer/network_test.go index c7ba5187f9..e589a48508 100644 --- a/peer/network_test.go +++ b/peer/network_test.go @@ -22,6 +22,8 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/version" "github.com/stretchr/testify/assert" + + ethcommon "github.com/ethereum/go-ethereum/common" ) var ( @@ -43,11 +45,14 @@ var ( _ common.AppSender = testAppSender{} _ message.GossipMessage = HelloGossip{} _ message.GossipHandler = &testGossipHandler{} + + _ message.CrossChainRequest = &ExampleCrossChainRequest{} + _ message.CrossChainRequestHandler = &testCrossChainHandler{} ) func TestNetworkDoesNotConnectToItself(t *testing.T) { selfNodeID := ids.GenerateTestNodeID() - n := NewNetwork(nil, nil, selfNodeID, 1) + n := NewNetwork(nil, nil, nil, selfNodeID, 1, 1) assert.NoError(t, n.Connected(context.Background(), selfNodeID, defaultPeerVersion)) assert.EqualValues(t, 0, n.Size()) } @@ -82,7 +87,8 @@ func TestRequestAnyRequestsRoutingAndResponse(t *testing.T) { } codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) - net = NewNetwork(sender, codecManager, ids.EmptyNodeID, 16) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) nodeID := ids.GenerateTestNodeID() @@ -104,7 +110,7 @@ func TestRequestAnyRequestsRoutingAndResponse(t *testing.T) { defer wg.Done() requestBytes, err := message.RequestToBytes(codecManager, requestMessage) assert.NoError(t, err) - responseBytes, _, err := client.RequestAny(defaultPeerVersion, requestBytes) + responseBytes, _, err := client.SendAppRequestAny(defaultPeerVersion, requestBytes) assert.NoError(t, err) assert.NotNil(t, responseBytes) @@ -156,7 +162,8 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { } codecManager := buildCodec(t, HelloRequest{}, HelloResponse{}) - net = NewNetwork(sender, codecManager, ids.EmptyNodeID, 16) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 16, 16) net.SetRequestHandler(&HelloGreetingRequestHandler{codec: codecManager}) client := NewNetworkClient(net) @@ -188,7 +195,7 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { defer wg.Done() requestBytes, err := message.RequestToBytes(codecManager, requestMessage) assert.NoError(t, err) - responseBytes, err := client.Request(nodeID, requestBytes) + responseBytes, err := client.SendAppRequest(nodeID, requestBytes) assert.NoError(t, err) assert.NotNil(t, responseBytes) @@ -210,7 +217,7 @@ func TestRequestRequestsRoutingAndResponse(t *testing.T) { } // ensure empty nodeID is not allowed - _, err := client.Request(ids.EmptyNodeID, []byte("hello there")) + _, err := client.SendAppRequest(ids.EmptyNodeID, []byte("hello there")) assert.Error(t, err) assert.Contains(t, err.Error(), "cannot send request to empty nodeID") } @@ -242,7 +249,8 @@ func TestRequestMinVersion(t *testing.T) { } // passing nil as codec works because the net.AppRequest is never called - net = NewNetwork(sender, codecManager, ids.EmptyNodeID, 1) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 16) client := NewNetworkClient(net) requestMessage := TestMessage{Message: "this is a request"} requestBytes, err := message.RequestToBytes(codecManager, requestMessage) @@ -260,7 +268,7 @@ func TestRequestMinVersion(t *testing.T) { ) // ensure version does not match - responseBytes, _, err := client.RequestAny( + responseBytes, _, err := client.SendAppRequestAny( &version.Application{ Major: 2, Minor: 0, @@ -272,7 +280,7 @@ func TestRequestMinVersion(t *testing.T) { assert.Nil(t, responseBytes) // ensure version matches and the request goes through - responseBytes, _, err = client.RequestAny(defaultPeerVersion, requestBytes) + responseBytes, _, err = client.SendAppRequestAny(defaultPeerVersion, requestBytes) assert.NoError(t, err) var response TestMessage @@ -296,6 +304,7 @@ func TestOnRequestHonoursDeadline(t *testing.T) { } codecManager := buildCodec(t, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) requestBytes, err := marshalStruct(codecManager, TestMessage{Message: "hello there"}) assert.NoError(t, err) @@ -303,7 +312,8 @@ func TestOnRequestHonoursDeadline(t *testing.T) { requestHandler := &testRequestHandler{ processingDuration: 500 * time.Millisecond, } - net = NewNetwork(sender, codecManager, ids.EmptyNodeID, 1) + + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) net.SetRequestHandler(requestHandler) nodeID := ids.GenerateTestNodeID() @@ -323,6 +333,7 @@ func TestOnRequestHonoursDeadline(t *testing.T) { func TestGossip(t *testing.T) { codecManager := buildCodec(t, HelloGossip{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) nodeID := ids.GenerateTestNodeID() var clientNetwork Network @@ -342,7 +353,7 @@ func TestGossip(t *testing.T) { } gossipHandler := &testGossipHandler{} - clientNetwork = NewNetwork(sender, codecManager, ids.EmptyNodeID, 1) + clientNetwork = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) clientNetwork.SetGossipHandler(gossipHandler) assert.NoError(t, clientNetwork.Connected(context.Background(), nodeID, defaultPeerVersion)) @@ -363,12 +374,13 @@ func TestGossip(t *testing.T) { func TestHandleInvalidMessages(t *testing.T) { codecManager := buildCodec(t, HelloGossip{}, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) nodeID := ids.GenerateTestNodeID() requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(sender, codecManager, ids.EmptyNodeID, 1) + clientNetwork := NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) clientNetwork.SetGossipHandler(message.NoopMempoolGossipHandler{}) clientNetwork.SetRequestHandler(&testRequestHandler{}) @@ -412,12 +424,13 @@ func TestHandleInvalidMessages(t *testing.T) { func TestNetworkPropagatesRequestHandlerError(t *testing.T) { codecManager := buildCodec(t, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) nodeID := ids.GenerateTestNodeID() requestID := uint32(1) sender := testAppSender{} - clientNetwork := NewNetwork(sender, codecManager, ids.EmptyNodeID, 1) + clientNetwork := NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) clientNetwork.SetGossipHandler(message.NoopMempoolGossipHandler{}) clientNetwork.SetRequestHandler(&testRequestHandler{err: errors.New("fail")}) // Return an error from the request handler @@ -433,6 +446,125 @@ func TestNetworkPropagatesRequestHandlerError(t *testing.T) { assert.Error(t, clientNetwork.AppRequest(context.Background(), nodeID, requestID, time.Now().Add(time.Second), requestMessage)) } +func TestCrossChainAppRequest(t *testing.T) { + var net Network + codecManager := buildCodec(t, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) + + sender := testAppSender{ + sendCrossChainAppRequestFn: func(requestingChainID ids.ID, requestID uint32, requestBytes []byte) error { + go func() { + if err := net.CrossChainAppRequest(context.Background(), requestingChainID, requestID, time.Now().Add(5*time.Second), requestBytes); err != nil { + panic(err) + } + }() + return nil + }, + sendCrossChainAppResponseFn: func(respondingChainID ids.ID, requestID uint32, responseBytes []byte) error { + go func() { + if err := net.CrossChainAppResponse(context.Background(), respondingChainID, requestID, responseBytes); err != nil { + panic(err) + } + }() + return nil + }, + } + + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) + client := NewNetworkClient(net) + + exampleCrossChainRequest := ExampleCrossChainRequest{ + Message: "hello this is an example request", + } + + crossChainRequest, err := buildCrossChainRequest(crossChainCodecManager, exampleCrossChainRequest) + assert.NoError(t, err) + + chainID := ids.ID(ethcommon.BytesToHash([]byte{1, 2, 3, 4, 5})) + responseBytes, err := client.SendCrossChainRequest(chainID, crossChainRequest) + assert.NoError(t, err) + + var response ExampleCrossChainResponse + if _, err = crossChainCodecManager.Unmarshal(responseBytes, &response); err != nil { + t.Fatal("unexpected error during unmarshal", err) + } + assert.Equal(t, "this is an example response", response.Response) +} + +func TestCrossChainRequestRequestsRoutingAndResponse(t *testing.T) { + var ( + callNum uint32 + senderWg sync.WaitGroup + net Network + ) + + sender := testAppSender{ + sendCrossChainAppRequestFn: func(requestingChainID ids.ID, requestID uint32, requestBytes []byte) error { + senderWg.Add(1) + go func() { + defer senderWg.Done() + if err := net.CrossChainAppRequest(context.Background(), requestingChainID, requestID, time.Now().Add(5*time.Second), requestBytes); err != nil { + panic(err) + } + }() + return nil + }, + sendCrossChainAppResponseFn: func(respondingChainID ids.ID, requestID uint32, responseBytes []byte) error { + senderWg.Add(1) + go func() { + defer senderWg.Done() + if err := net.CrossChainAppResponse(context.Background(), respondingChainID, requestID, responseBytes); err != nil { + panic(err) + } + atomic.AddUint32(&callNum, 1) + }() + return nil + }, + } + + codecManager := buildCodec(t, TestMessage{}) + crossChainCodecManager := buildCodec(t, ExampleCrossChainRequest{}, ExampleCrossChainResponse{}) + net = NewNetwork(sender, codecManager, crossChainCodecManager, ids.EmptyNodeID, 1, 1) + net.SetCrossChainRequestHandler(&testCrossChainHandler{codec: crossChainCodecManager}) + client := NewNetworkClient(net) + + exampleCrossChainRequest := ExampleCrossChainRequest{ + Message: "hello this is an example request", + } + + chainID := ids.ID(ethcommon.BytesToHash([]byte{1, 2, 3, 4, 5})) + defer net.Shutdown() + + totalRequests := 500 + numCallsPerRequest := 1 // on sending response + totalCalls := totalRequests * numCallsPerRequest + + var requestWg sync.WaitGroup + requestWg.Add(totalCalls) + + for i := 0; i < totalCalls; i++ { + go func() { + defer requestWg.Done() + crossChainRequest, err := buildCrossChainRequest(crossChainCodecManager, exampleCrossChainRequest) + assert.NoError(t, err) + responseBytes, err := client.SendCrossChainRequest(chainID, crossChainRequest) + assert.NoError(t, err) + assert.NotNil(t, responseBytes) + + var response ExampleCrossChainResponse + if _, err = crossChainCodecManager.Unmarshal(responseBytes, &response); err != nil { + panic(fmt.Errorf("unexpected error during unmarshal: %w", err)) + } + assert.Equal(t, "this is an example response", response.Response) + }() + } + + requestWg.Wait() + senderWg.Wait() + assert.Equal(t, totalCalls, int(atomic.LoadUint32(&callNum))) +} + func buildCodec(t *testing.T, types ...interface{}) codec.Manager { codecManager := codec.NewDefaultManager() c := linearcodec.NewDefault() @@ -453,6 +585,10 @@ func buildGossip(codec codec.Manager, msg message.GossipMessage) ([]byte, error) return codec.Marshal(message.Version, &msg) } +func buildCrossChainRequest(codec codec.Manager, msg message.CrossChainRequest) ([]byte, error) { + return codec.Marshal(message.Version, &msg) +} + type testAppSender struct { sendCrossChainAppRequestFn func(ids.ID, uint32, []byte) error sendCrossChainAppResponseFn func(ids.ID, uint32, []byte) error @@ -600,3 +736,32 @@ func (r *testRequestHandler) handleTestRequest(ctx context.Context, _ ids.NodeID } return r.response, r.err } + +type ExampleCrossChainRequest struct { + Message string `serialize:"true"` +} + +func (e ExampleCrossChainRequest) Handle(ctx context.Context, requestingChainID ids.ID, requestID uint32, handler message.CrossChainRequestHandler) ([]byte, error) { + return handler.(*testCrossChainHandler).HandleCrossChainRequest(ctx, requestingChainID, requestID, e) +} + +func (e ExampleCrossChainRequest) String() string { + return fmt.Sprintf("TestMessage(%s)", e.Message) +} + +type ExampleCrossChainResponse struct { + Response string `serialize:"true"` +} + +type TestCrossChainRequestHandler interface { + HandleCrossChainRequest(ctx context.Context, requestingchainID ids.ID, requestID uint32, exampleRequest message.CrossChainRequest) ([]byte, error) +} + +type testCrossChainHandler struct { + message.CrossChainRequestHandler + codec codec.Manager +} + +func (t *testCrossChainHandler) HandleCrossChainRequest(ctx context.Context, requestingChainID ids.ID, requestID uint32, exampleRequest message.CrossChainRequest) ([]byte, error) { + return t.codec.Marshal(message.Version, ExampleCrossChainResponse{Response: "this is an example response"}) +} diff --git a/peer/stats/stats.go b/peer/stats/stats.go index 2c1eea6713..e29a26e614 100644 --- a/peer/stats/stats.go +++ b/peer/stats/stats.go @@ -9,8 +9,7 @@ import ( "github.com/ava-labs/subnet-evm/metrics" ) -// RequestHandlerStats provides the interface for metrics on request handling. -// Since we drop +// RequestHandlerStats provides the interface for metrics for both app requests and cross chain requests. type RequestHandlerStats interface { UpdateTimeUntilDeadline(duration time.Duration) IncDeadlineDroppedRequest() @@ -35,3 +34,10 @@ func NewRequestHandlerStats() RequestHandlerStats { droppedRequests: metrics.GetOrRegisterCounter("net_req_deadline_dropped", nil), } } + +func NewCrossChainRequestHandlerStats() RequestHandlerStats { + return &requestHandlerStats{ + timeUntilDeadline: metrics.GetOrRegisterTimer("net_cross_chain_req_time_until_deadline", nil), + droppedRequests: metrics.GetOrRegisterCounter("net_cross_chain_req_deadline_dropped", nil), + } +} diff --git a/peer/waiting_handler.go b/peer/waiting_handler.go index 8162817f56..846166c121 100644 --- a/peer/waiting_handler.go +++ b/peer/waiting_handler.go @@ -4,7 +4,6 @@ package peer import ( - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/subnet-evm/plugin/evm/message" ) @@ -20,14 +19,14 @@ type waitingResponseHandler struct { } // OnResponse passes the response bytes to the responseChan and closes the channel -func (w *waitingResponseHandler) OnResponse(_ ids.NodeID, _ uint32, response []byte) error { +func (w *waitingResponseHandler) OnResponse(response []byte) error { w.responseChan <- response close(w.responseChan) return nil } // OnFailure sets the failed flag to true and closes the channel -func (w *waitingResponseHandler) OnFailure(ids.NodeID, uint32) error { +func (w *waitingResponseHandler) OnFailure() error { w.failed = true close(w.responseChan) return nil diff --git a/plugin/evm/config.go b/plugin/evm/config.go index 74f6a24be5..6abaed1093 100644 --- a/plugin/evm/config.go +++ b/plugin/evm/config.go @@ -15,37 +15,38 @@ import ( ) const ( - defaultAcceptorQueueLimit = 64 // Provides 2 minutes of buffer (2s block target) for a commit delay - defaultPruningEnabled = true - defaultCommitInterval = 4096 - defaultTrieCleanCache = 512 - defaultTrieDirtyCache = 256 - defaultTrieDirtyCommitTarget = 20 - defaultSnapshotCache = 256 - defaultSyncableCommitInterval = defaultCommitInterval * 4 - defaultSnapshotAsync = true - defaultRpcGasCap = 50_000_000 // Default to 50M Gas Limit - defaultRpcTxFeeCap = 100 // 100 AVAX - defaultMetricsExpensiveEnabled = true - defaultApiMaxDuration = 0 // Default to no maximum API call duration - defaultWsCpuRefillRate = 0 // Default to no maximum WS CPU usage - defaultWsCpuMaxStored = 0 // Default to no maximum WS CPU usage - defaultMaxBlocksPerRequest = 0 // Default to no maximum on the number of blocks per getLogs request - defaultContinuousProfilerFrequency = 15 * time.Minute - defaultContinuousProfilerMaxFiles = 5 - defaultRegossipFrequency = 1 * time.Minute - defaultRegossipMaxTxs = 16 - defaultRegossipTxsPerAddress = 1 - defaultPriorityRegossipFrequency = 1 * time.Second - defaultPriorityRegossipMaxTxs = 32 - defaultPriorityRegossipTxsPerAddress = 16 - defaultOfflinePruningBloomFilterSize uint64 = 512 // Default size (MB) for the offline pruner to use - defaultLogLevel = "info" - defaultLogJSONFormat = false - defaultMaxOutboundActiveRequests = 16 - defaultPopulateMissingTriesParallelism = 1024 - defaultStateSyncServerTrieCache = 64 // MB - defaultAcceptedCacheSize = 32 // blocks + defaultAcceptorQueueLimit = 64 // Provides 2 minutes of buffer (2s block target) for a commit delay + defaultPruningEnabled = true + defaultCommitInterval = 4096 + defaultTrieCleanCache = 512 + defaultTrieDirtyCache = 256 + defaultTrieDirtyCommitTarget = 20 + defaultSnapshotCache = 256 + defaultSyncableCommitInterval = defaultCommitInterval * 4 + defaultSnapshotAsync = true + defaultRpcGasCap = 50_000_000 // Default to 50M Gas Limit + defaultRpcTxFeeCap = 100 // 100 AVAX + defaultMetricsExpensiveEnabled = true + defaultApiMaxDuration = 0 // Default to no maximum API call duration + defaultWsCpuRefillRate = 0 // Default to no maximum WS CPU usage + defaultWsCpuMaxStored = 0 // Default to no maximum WS CPU usage + defaultMaxBlocksPerRequest = 0 // Default to no maximum on the number of blocks per getLogs request + defaultContinuousProfilerFrequency = 15 * time.Minute + defaultContinuousProfilerMaxFiles = 5 + defaultRegossipFrequency = 1 * time.Minute + defaultRegossipMaxTxs = 16 + defaultRegossipTxsPerAddress = 1 + defaultPriorityRegossipFrequency = 1 * time.Second + defaultPriorityRegossipMaxTxs = 32 + defaultPriorityRegossipTxsPerAddress = 16 + defaultOfflinePruningBloomFilterSize uint64 = 512 // Default size (MB) for the offline pruner to use + defaultLogLevel = "info" + defaultLogJSONFormat = false + defaultMaxOutboundActiveRequests = 16 + defaultMaxOutboundActiveCrossChainRequests = 64 + defaultPopulateMissingTriesParallelism = 1024 + defaultStateSyncServerTrieCache = 64 // MB + defaultAcceptedCacheSize = 32 // blocks // defaultStateSyncMinBlocks is the minimum number of blocks the blockchain // should be ahead of local last accepted to perform state sync. @@ -83,6 +84,7 @@ type Config struct { // Subnet EVM APIs SnowmanAPIEnabled bool `json:"snowman-api-enabled"` + WarpAPIEnabled bool `json:"warp-api-enabled"` AdminAPIEnabled bool `json:"admin-api-enabled"` AdminAPIDir string `json:"admin-api-dir"` @@ -171,7 +173,11 @@ type Config struct { OfflinePruningDataDirectory string `json:"offline-pruning-data-directory"` // VM2VM network - MaxOutboundActiveRequests int64 `json:"max-outbound-active-requests"` + MaxOutboundActiveRequests int64 `json:"max-outbound-active-requests"` + MaxOutboundActiveCrossChainRequests int64 `json:"max-outbound-active-cross-chain-requests"` + + // Database Settings + InspectDatabase bool `json:"inspect-database"` // Inspects the database on startup if enabled. // Sync settings StateSyncEnabled bool `json:"state-sync-enabled"` @@ -187,12 +193,21 @@ type Config struct { // identical state with the pre-upgrade ruleset. SkipUpgradeCheck bool `json:"skip-upgrade-check"` + // SkipSubnetEVMUpgradeCheck disables checking that SubnetEVM Upgrade is enabled at genesis + SkipSubnetEVMUpgradeCheck bool `json:"skip-subnet-evm-upgrade-check"` + // AcceptedCacheSize is the depth to keep in the accepted headers cache and the // accepted logs cache at the accepted tip. // // This is particularly useful for improving the performance of eth_getLogs // on RPC nodes. AcceptedCacheSize int `json:"accepted-cache-size"` + + // TxLookupLimit is the maximum number of blocks from head whose tx indices + // are reserved: + // * 0: means no limit + // * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes + TxLookupLimit uint64 `json:"tx-lookup-limit"` } // EthAPIs returns an array of strings representing the Eth APIs that should be enabled @@ -243,6 +258,7 @@ func (c *Config) SetDefaults() { c.LogLevel = defaultLogLevel c.LogJSONFormat = defaultLogJSONFormat c.MaxOutboundActiveRequests = defaultMaxOutboundActiveRequests + c.MaxOutboundActiveCrossChainRequests = defaultMaxOutboundActiveCrossChainRequests c.PopulateMissingTriesParallelism = defaultPopulateMissingTriesParallelism c.StateSyncServerTrieCache = defaultStateSyncServerTrieCache c.StateSyncCommitInterval = defaultSyncableCommitInterval diff --git a/plugin/evm/config_test.go b/plugin/evm/config_test.go index fd92065741..53a7777423 100644 --- a/plugin/evm/config_test.go +++ b/plugin/evm/config_test.go @@ -72,6 +72,34 @@ func TestUnmarshalConfig(t *testing.T) { Config{StateSyncIDs: "NodeID-CaBYJ9kzHvrQFiYWowMkJGAQKGMJqZoat"}, false, }, + { + "empty tx lookup limit", + []byte(`{}`), + Config{TxLookupLimit: 0}, + false, + }, + { + "zero tx lookup limit", + []byte(`{"tx-lookup-limit": 0}`), + func() Config { + return Config{TxLookupLimit: 0} + }(), + false, + }, + { + "1 tx lookup limit", + []byte(`{"tx-lookup-limit": 1}`), + func() Config { + return Config{TxLookupLimit: 1} + }(), + false, + }, + { + "-1 tx lookup limit", + []byte(`{"tx-lookup-limit": -1}`), + Config{}, + true, + }, { "allow unprotected tx hashes", []byte(`{"allow-unprotected-tx-hashes": ["0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c"]}`), diff --git a/plugin/evm/gossiper.go b/plugin/evm/gossiper.go index 300958b78d..bc19021ebf 100644 --- a/plugin/evm/gossiper.go +++ b/plugin/evm/gossiper.go @@ -62,7 +62,7 @@ type pushGossiper struct { // [recentTxs] prevent us from over-gossiping the // same transaction in a short period of time. - recentTxs *cache.LRU + recentTxs *cache.LRU[common.Hash, interface{}] codec codec.Manager signer types.Signer @@ -86,7 +86,7 @@ func (vm *VM) createGossiper(stats GossipStats) Gossiper { txsToGossip: make(map[common.Hash]*types.Transaction), shutdownChan: vm.shutdownChan, shutdownWg: &vm.shutdownWg, - recentTxs: &cache.LRU{Size: recentCacheSize}, + recentTxs: &cache.LRU[common.Hash, interface{}]{Size: recentCacheSize}, codec: vm.networkCodec, signer: types.LatestSigner(vm.blockChain.Config()), stats: stats, diff --git a/plugin/evm/message/codec.go b/plugin/evm/message/codec.go index 8b0fabd550..8b9a84d43a 100644 --- a/plugin/evm/message/codec.go +++ b/plugin/evm/message/codec.go @@ -15,7 +15,10 @@ const ( maxMessageSize = 1 * units.MiB ) -var Codec codec.Manager +var ( + Codec codec.Manager + CrossChainCodec codec.Manager +) func init() { Codec = codec.NewManager(maxMessageSize) @@ -37,10 +40,30 @@ func init() { c.RegisterType(CodeRequest{}), c.RegisterType(CodeResponse{}), + // Warp request types + c.RegisterType(SignatureRequest{}), + c.RegisterType(SignatureResponse{}), + Codec.RegisterCodec(Version, c), ) if errs.Errored() { panic(errs.Err) } + + CrossChainCodec = codec.NewManager(maxMessageSize) + ccc := linearcodec.NewDefault() + + errs = wrappers.Errs{} + errs.Add( + // CrossChainRequest Types + ccc.RegisterType(EthCallRequest{}), + ccc.RegisterType(EthCallResponse{}), + + CrossChainCodec.RegisterCodec(Version, ccc), + ) + + if errs.Errored() { + panic(errs.Err) + } } diff --git a/plugin/evm/message/cross_chain_handler.go b/plugin/evm/message/cross_chain_handler.go new file mode 100644 index 0000000000..f356684ee3 --- /dev/null +++ b/plugin/evm/message/cross_chain_handler.go @@ -0,0 +1,73 @@ +// (c) 2021-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "context" + "encoding/json" + + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" + + "github.com/ava-labs/subnet-evm/internal/ethapi" + "github.com/ava-labs/subnet-evm/rpc" + + "github.com/ethereum/go-ethereum/log" +) + +var _ CrossChainRequestHandler = &crossChainHandler{} + +// crossChainHandler implements the CrossChainRequestHandler interface +type crossChainHandler struct { + backend ethapi.Backend + crossChainCodec codec.Manager +} + +// NewCrossChainHandler creates and returns a new instance of CrossChainRequestHandler +func NewCrossChainHandler(b ethapi.Backend, codec codec.Manager) CrossChainRequestHandler { + return &crossChainHandler{ + backend: b, + crossChainCodec: codec, + } +} + +// HandleEthCallRequests returns an encoded EthCallResponse to the given [ethCallRequest] +// This function executes EVM Call against the state associated with [rpc.AcceptedBlockNumber] with the given +// transaction call object [ethCallRequest]. +// This function does not return an error as errors are treated as FATAL to the node. +func (c *crossChainHandler) HandleEthCallRequest(ctx context.Context, requestingChainID ids.ID, requestID uint32, ethCallRequest EthCallRequest) ([]byte, error) { + lastAcceptedBlockNumber := rpc.BlockNumber(c.backend.LastAcceptedBlock().NumberU64()) + lastAcceptedBlockNumberOrHash := rpc.BlockNumberOrHash{BlockNumber: &lastAcceptedBlockNumber} + + transactionArgs := ethapi.TransactionArgs{} + err := json.Unmarshal(ethCallRequest.RequestArgs, &transactionArgs) + if err != nil { + log.Debug("error occurred with JSON unmarshalling ethCallRequest.RequestArgs", "err", err) + return nil, nil + } + + result, err := ethapi.DoCall(ctx, c.backend, transactionArgs, lastAcceptedBlockNumberOrHash, nil, c.backend.RPCEVMTimeout(), c.backend.RPCGasCap()) + if err != nil { + log.Debug("error occurred with EthCall", "err", err, "transactionArgs", ethCallRequest.RequestArgs, "blockNumberOrHash", lastAcceptedBlockNumberOrHash) + return nil, nil + } + + executionResult, err := json.Marshal(&result) + if err != nil { + log.Debug("error occurred with JSON marshalling result", "err", err) + return nil, nil + } + + response := EthCallResponse{ + ExecutionResult: executionResult, + } + + responseBytes, err := c.crossChainCodec.Marshal(Version, response) + if err != nil { + log.Warn("error occurred with marshalling EthCallResponse", "err", err, "EthCallResponse", response) + return nil, nil + } + + return responseBytes, nil +} diff --git a/plugin/evm/message/eth_call_request.go b/plugin/evm/message/eth_call_request.go new file mode 100644 index 0000000000..69d1139a2b --- /dev/null +++ b/plugin/evm/message/eth_call_request.go @@ -0,0 +1,33 @@ +// (c) 2021-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" +) + +var _ CrossChainRequest = EthCallRequest{} + +// EthCallRequest has the JSON Data necessary to execute a new EVM call on the blockchain +type EthCallRequest struct { + RequestArgs []byte `serialize:"true"` +} + +// EthCallResponse represents the JSON return value of the executed EVM call +type EthCallResponse struct { + ExecutionResult []byte `serialize:"true"` +} + +// String converts EthCallRequest to a string +func (e EthCallRequest) String() string { + return fmt.Sprintf("%#v", e) +} + +// Handle returns the encoded EthCallResponse by executing EVM call with the given EthCallRequest +func (e EthCallRequest) Handle(ctx context.Context, requestingChainID ids.ID, requestID uint32, handler CrossChainRequestHandler) ([]byte, error) { + return handler.HandleEthCallRequest(ctx, requestingChainID, requestID, e) +} diff --git a/plugin/evm/message/handler.go b/plugin/evm/message/handler.go index 4b7bf7c4d7..659908aaee 100644 --- a/plugin/evm/message/handler.go +++ b/plugin/evm/message/handler.go @@ -12,8 +12,9 @@ import ( ) var ( - _ GossipHandler = NoopMempoolGossipHandler{} - _ RequestHandler = NoopRequestHandler{} + _ GossipHandler = NoopMempoolGossipHandler{} + _ RequestHandler = NoopRequestHandler{} + _ CrossChainRequestHandler = NoopCrossChainRequestHandler{} ) // GossipHandler handles incoming gossip messages @@ -35,17 +36,18 @@ func (NoopMempoolGossipHandler) HandleTxs(nodeID ids.NodeID, _ TxsGossip) error // Also see GossipHandler for implementation style. type RequestHandler interface { HandleTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest LeafsRequest) ([]byte, error) - HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, request BlockRequest) ([]byte, error) + HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest BlockRequest) ([]byte, error) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest CodeRequest) ([]byte, error) + HandleSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest SignatureRequest) ([]byte, error) } // ResponseHandler handles response for a sent request // Only one of OnResponse or OnFailure is called for a given requestID, not both type ResponseHandler interface { // OnResponse is invoked when the peer responded to a request - OnResponse(nodeID ids.NodeID, requestID uint32, response []byte) error + OnResponse(response []byte) error // OnFailure is invoked when there was a failure in processing a request - OnFailure(nodeID ids.NodeID, requestID uint32) error + OnFailure() error } type NoopRequestHandler struct{} @@ -61,3 +63,18 @@ func (NoopRequestHandler) HandleBlockRequest(ctx context.Context, nodeID ids.Nod func (NoopRequestHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest CodeRequest) ([]byte, error) { return nil, nil } + +func (NoopRequestHandler) HandleSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest SignatureRequest) ([]byte, error) { + return nil, nil +} + +// CrossChainRequestHandler interface handles incoming requests from another chain +type CrossChainRequestHandler interface { + HandleEthCallRequest(ctx context.Context, requestingchainID ids.ID, requestID uint32, ethCallRequest EthCallRequest) ([]byte, error) +} + +type NoopCrossChainRequestHandler struct{} + +func (NoopCrossChainRequestHandler) HandleEthCallRequest(ctx context.Context, requestingchainID ids.ID, requestID uint32, ethCallRequest EthCallRequest) ([]byte, error) { + return nil, nil +} diff --git a/plugin/evm/message/request.go b/plugin/evm/message/request.go index 2aadf5a902..6b5831f9df 100644 --- a/plugin/evm/message/request.go +++ b/plugin/evm/message/request.go @@ -34,3 +34,13 @@ func BytesToRequest(codec codec.Manager, requestBytes []byte) (Request, error) { func RequestToBytes(codec codec.Manager, request Request) ([]byte, error) { return codec.Marshal(Version, &request) } + +// CrossChainRequest represents the interface a cross chain request should implement +type CrossChainRequest interface { + // CrossChainRequest should implement String() for logging. + fmt.Stringer + + // Handle allows [CrossChainRequest] to call respective methods on handler to handle + // this particular request type + Handle(ctx context.Context, requestingChainID ids.ID, requestID uint32, handler CrossChainRequestHandler) ([]byte, error) +} diff --git a/plugin/evm/message/signature_request.go b/plugin/evm/message/signature_request.go new file mode 100644 index 0000000000..3ed8b64829 --- /dev/null +++ b/plugin/evm/message/signature_request.go @@ -0,0 +1,33 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/bls" +) + +var _ Request = SignatureRequest{} + +// SignatureRequest is used to request a warp message's signature. +type SignatureRequest struct { + MessageID ids.ID `serialize:"true"` +} + +func (s SignatureRequest) String() string { + return fmt.Sprintf("SignatureRequest(MessageID=%s)", s.MessageID.String()) +} + +func (s SignatureRequest) Handle(ctx context.Context, nodeID ids.NodeID, requestID uint32, handler RequestHandler) ([]byte, error) { + return handler.HandleSignatureRequest(ctx, nodeID, requestID, s) +} + +// SignatureResponse is the response to a SignatureRequest. +// The response contains a BLS signature of the requested message, signed by the responding node's BLS private key. +type SignatureResponse struct { + Signature [bls.SignatureLen]byte `serialize:"true"` +} diff --git a/plugin/evm/message/signature_request_test.go b/plugin/evm/message/signature_request_test.go new file mode 100644 index 0000000000..9e4c2fd96e --- /dev/null +++ b/plugin/evm/message/signature_request_test.go @@ -0,0 +1,60 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package message + +import ( + "encoding/base64" + "encoding/hex" + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/stretchr/testify/require" +) + +// TestMarshalSignatureRequest asserts that the structure or serialization logic hasn't changed, primarily to +// ensure compatibility with the network. +func TestMarshalSignatureRequest(t *testing.T) { + messageIDBytes, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + messageID, err := ids.ToID(messageIDBytes) + require.NoError(t, err) + + signatureRequest := SignatureRequest{ + MessageID: messageID, + } + + base64SignatureRequest := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" + signatureRequestBytes, err := Codec.Marshal(Version, signatureRequest) + require.NoError(t, err) + require.Equal(t, base64SignatureRequest, base64.StdEncoding.EncodeToString(signatureRequestBytes)) + + var s SignatureRequest + _, err = Codec.Unmarshal(signatureRequestBytes, &s) + require.NoError(t, err) + require.Equal(t, signatureRequest.MessageID, s.MessageID) +} + +// TestMarshalSignatureResponse asserts that the structure or serialization logic hasn't changed, primarily to +// ensure compatibility with the network. +func TestMarshalSignatureResponse(t *testing.T) { + var signature [bls.SignatureLen]byte + sig, err := hex.DecodeString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef") + require.NoError(t, err, "failed to decode string to hex") + + copy(signature[:], sig) + signatureResponse := SignatureResponse{ + Signature: signature, + } + + base64SignatureResponse := "AAABI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8=" + signatureResponseBytes, err := Codec.Marshal(Version, signatureResponse) + require.NoError(t, err) + require.Equal(t, base64SignatureResponse, base64.StdEncoding.EncodeToString(signatureResponseBytes)) + + var s SignatureResponse + _, err = Codec.Unmarshal(signatureResponseBytes, &s) + require.NoError(t, err) + require.Equal(t, signatureResponse.Signature, s.Signature) +} diff --git a/plugin/evm/message/syncable.go b/plugin/evm/message/syncable.go index c9b73632ed..f79a9f2dfa 100644 --- a/plugin/evm/message/syncable.go +++ b/plugin/evm/message/syncable.go @@ -25,10 +25,10 @@ type SyncSummary struct { summaryID ids.ID bytes []byte - acceptImpl func(SyncSummary) (bool, error) + acceptImpl func(SyncSummary) (block.StateSyncMode, error) } -func NewSyncSummaryFromBytes(summaryBytes []byte, acceptImpl func(SyncSummary) (bool, error)) (SyncSummary, error) { +func NewSyncSummaryFromBytes(summaryBytes []byte, acceptImpl func(SyncSummary) (block.StateSyncMode, error)) (SyncSummary, error) { summary := SyncSummary{} if codecVersion, err := Codec.Unmarshal(summaryBytes, &summary); err != nil { return SyncSummary{}, err @@ -83,9 +83,9 @@ func (s SyncSummary) String() string { return fmt.Sprintf("SyncSummary(BlockHash=%s, BlockNumber=%d, BlockRoot=%s)", s.BlockHash, s.BlockNumber, s.BlockRoot) } -func (s SyncSummary) Accept(context.Context) (bool, error) { +func (s SyncSummary) Accept(context.Context) (block.StateSyncMode, error) { if s.acceptImpl == nil { - return false, fmt.Errorf("accept implementation not specified for summary: %s", s) + return block.StateSyncSkipped, fmt.Errorf("accept implementation not specified for summary: %s", s) } return s.acceptImpl(s) } diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go new file mode 100644 index 0000000000..529249fbc1 --- /dev/null +++ b/plugin/evm/network_handler.go @@ -0,0 +1,60 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package evm + +import ( + "context" + + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/subnet-evm/plugin/evm/message" + syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers" + syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats" + "github.com/ava-labs/subnet-evm/trie" + warpHandlers "github.com/ava-labs/subnet-evm/warp/handlers" +) + +var _ message.RequestHandler = &networkHandler{} + +type networkHandler struct { + stateTrieLeafsRequestHandler *syncHandlers.LeafsRequestHandler + blockRequestHandler *syncHandlers.BlockRequestHandler + codeRequestHandler *syncHandlers.CodeRequestHandler + signatureRequestHandler warpHandlers.SignatureRequestHandler +} + +// newNetworkHandler constructs the handler for serving network requests. +func newNetworkHandler( + provider syncHandlers.SyncDataProvider, + evmTrieDB *trie.Database, + networkCodec codec.Manager, +) message.RequestHandler { + syncStats := syncStats.NewHandlerStats(metrics.Enabled) + return &networkHandler{ + // State sync handlers + stateTrieLeafsRequestHandler: syncHandlers.NewLeafsRequestHandler(evmTrieDB, provider, networkCodec, syncStats), + blockRequestHandler: syncHandlers.NewBlockRequestHandler(provider, networkCodec, syncStats), + codeRequestHandler: syncHandlers.NewCodeRequestHandler(evmTrieDB.DiskDB(), networkCodec, syncStats), + + // TODO: initialize actual signature request handler when warp is ready + signatureRequestHandler: &warpHandlers.NoopSignatureRequestHandler{}, + } +} + +func (n networkHandler) HandleTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest message.LeafsRequest) ([]byte, error) { + return n.stateTrieLeafsRequestHandler.OnLeafsRequest(ctx, nodeID, requestID, leafsRequest) +} + +func (n networkHandler) HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest message.BlockRequest) ([]byte, error) { + return n.blockRequestHandler.OnBlockRequest(ctx, nodeID, requestID, blockRequest) +} + +func (n networkHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest message.CodeRequest) ([]byte, error) { + return n.codeRequestHandler.OnCodeRequest(ctx, nodeID, requestID, codeRequest) +} + +func (n networkHandler) HandleSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest message.SignatureRequest) ([]byte, error) { + return n.signatureRequestHandler.OnSignatureRequest(ctx, nodeID, requestID, signatureRequest) +} diff --git a/plugin/evm/syncervm_client.go b/plugin/evm/syncervm_client.go index b56f1a30c6..bf185900c0 100644 --- a/plugin/evm/syncervm_client.go +++ b/plugin/evm/syncervm_client.go @@ -154,7 +154,7 @@ func (client *stateSyncerClient) stateSync(ctx context.Context) error { // acceptSyncSummary returns true if sync will be performed and launches the state sync process // in a goroutine. -func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncSummary) (bool, error) { +func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncSummary) (block.StateSyncMode, error) { isResume := proposedSummary.BlockHash == client.resumableSummary.BlockHash if !isResume { // Skip syncing if the blockchain is not significantly ahead of local state, @@ -167,12 +167,12 @@ func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncS "syncableHeight", proposedSummary.Height(), ) if err := client.StateSyncClearOngoingSummary(); err != nil { - return false, fmt.Errorf("failed to clear ongoing summary after skipping state sync: %w", err) + return block.StateSyncSkipped, fmt.Errorf("failed to clear ongoing summary after skipping state sync: %w", err) } // Initialize snapshots if we're skipping state sync, since it will not have been initialized on // startup. client.chain.BlockChain().InitializeSnapshots() - return false, nil + return block.StateSyncSkipped, nil } // Wipe the snapshot completely if we are not resuming from an existing sync, so that we do not @@ -193,10 +193,10 @@ func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncS // Note: this must be performed after WipeSnapshot finishes so that we do not start a state sync // session from a partially wiped snapshot. if err := client.metadataDB.Put(stateSyncSummaryKey, proposedSummary.Bytes()); err != nil { - return false, fmt.Errorf("failed to write state sync summary key to disk: %w", err) + return block.StateSyncSkipped, fmt.Errorf("failed to write state sync summary key to disk: %w", err) } if err := client.db.Commit(); err != nil { - return false, fmt.Errorf("failed to commit db: %w", err) + return block.StateSyncSkipped, fmt.Errorf("failed to commit db: %w", err) } log.Info("Starting state sync", "summary", proposedSummary) @@ -220,7 +220,7 @@ func (client *stateSyncerClient) acceptSyncSummary(proposedSummary message.SyncS log.Info("stateSync completed, notifying engine", "err", client.stateSyncErr) client.toEngine <- commonEng.StateSyncDone }() - return true, nil + return block.StateSyncStatic, nil } // syncBlocks fetches (up to) [parentsToGet] blocks from peers diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go index d67ab92df0..b5a37194cb 100644 --- a/plugin/evm/syncervm_test.go +++ b/plugin/evm/syncervm_test.go @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/choices" commonEng "github.com/ava-labs/avalanchego/snow/engine/common" + "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/subnet-evm/accounts/keystore" @@ -44,7 +45,7 @@ func TestSkipStateSync(t *testing.T) { test := syncTest{ syncableInterval: 256, stateSyncMinBlocks: 300, // must be greater than [syncableInterval] to skip sync - shouldSync: false, + syncMode: block.StateSyncSkipped, } vmSetup := createSyncServerAndClientVMs(t, test) defer vmSetup.Teardown(t) @@ -57,7 +58,7 @@ func TestStateSyncFromScratch(t *testing.T) { test := syncTest{ syncableInterval: 256, stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - shouldSync: true, + syncMode: block.StateSyncStatic, } vmSetup := createSyncServerAndClientVMs(t, test) defer vmSetup.Teardown(t) @@ -78,7 +79,7 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { test := syncTest{ syncableInterval: 256, stateSyncMinBlocks: 50, // must be less than [syncableInterval] to perform sync - shouldSync: true, + syncMode: block.StateSyncStatic, responseIntercept: func(syncerVM *VM, nodeID ids.NodeID, requestID uint32, response []byte) { lock.Lock() defer lock.Unlock() @@ -107,7 +108,7 @@ func TestStateSyncToggleEnabledToDisabled(t *testing.T) { // Perform sync resulting in early termination. testSyncerVM(t, vmSetup, test) - test.shouldSync = true + test.syncMode = block.StateSyncStatic test.responseIntercept = nil test.expectedErr = nil @@ -360,7 +361,7 @@ type syncTest struct { responseIntercept func(vm *VM, nodeID ids.NodeID, requestID uint32, response []byte) stateSyncMinBlocks uint64 syncableInterval uint64 - shouldSync bool + syncMode block.StateSyncMode expectedErr error } @@ -388,14 +389,14 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) { } assert.Equal(t, summary, retrievedSummary) - shouldSync, err := parsedSummary.Accept(context.Background()) + syncMode, err := parsedSummary.Accept(context.Background()) if err != nil { t.Fatal("unexpected error accepting state summary", "err", err) } - if shouldSync != test.shouldSync { - t.Fatal("unexpected value returned from accept", "expected", test.shouldSync, "got", shouldSync) + if syncMode != test.syncMode { + t.Fatal("unexpected value returned from accept", "expected", test.syncMode, "got", syncMode) } - if !shouldSync { + if syncMode == block.StateSyncSkipped { return } msg := <-syncerEngineChan diff --git a/plugin/evm/version.go b/plugin/evm/version.go index cfdcb2d22d..d889ba117f 100644 --- a/plugin/evm/version.go +++ b/plugin/evm/version.go @@ -11,7 +11,7 @@ var ( // GitCommit is set by the build script GitCommit string // Version is the version of Subnet EVM - Version string = "v0.4.7" + Version string = "v0.4.9" ) func init() { diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index 1721ed1f25..d48b7744c4 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -8,7 +8,6 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "os" "path/filepath" "strings" @@ -35,9 +34,8 @@ import ( "github.com/ava-labs/subnet-evm/rpc" statesyncclient "github.com/ava-labs/subnet-evm/sync/client" "github.com/ava-labs/subnet-evm/sync/client/stats" - "github.com/ava-labs/subnet-evm/sync/handlers" - handlerstats "github.com/ava-labs/subnet-evm/sync/handlers/stats" "github.com/ava-labs/subnet-evm/trie" + "github.com/ava-labs/subnet-evm/warp" // Force-load tracer engine to trigger registration // @@ -86,9 +84,10 @@ const ( // and fail verification maxFutureBlockTime = 10 * time.Second - decidedCacheSize = 100 - missingCacheSize = 50 - unverifiedCacheSize = 50 + decidedCacheSize = 100 + missingCacheSize = 50 + unverifiedCacheSize = 50 + warpSignatureCacheSize = 500 // Prefixes for metrics gatherers ethMetricsPrefix = "eth" @@ -107,17 +106,19 @@ var ( lastAcceptedKey = []byte("last_accepted_key") acceptedPrefix = []byte("snowman_accepted") metadataPrefix = []byte("metadata") + warpPrefix = []byte("warp") ethDBPrefix = []byte("ethdb") ) var ( - errEmptyBlock = errors.New("empty block") - errUnsupportedFXs = errors.New("unsupported feature extensions") - errInvalidBlock = errors.New("invalid block") - errInvalidNonce = errors.New("invalid nonce") - errUnclesUnsupported = errors.New("uncles unsupported") - errNilBaseFeeSubnetEVM = errors.New("nil base fee is invalid after subnetEVM") - errNilBlockGasCostSubnetEVM = errors.New("nil blockGasCost is invalid after subnetEVM") + errEmptyBlock = errors.New("empty block") + errUnsupportedFXs = errors.New("unsupported feature extensions") + errInvalidBlock = errors.New("invalid block") + errInvalidNonce = errors.New("invalid nonce") + errUnclesUnsupported = errors.New("uncles unsupported") + errNilBaseFeeSubnetEVM = errors.New("nil base fee is invalid after subnetEVM") + errNilBlockGasCostSubnetEVM = errors.New("nil blockGasCost is invalid after subnetEVM") + errSubnetEVMUpgradeNotEnabled = errors.New("SubnetEVM upgrade is not enabled in genesis") ) var originalStderr *os.File @@ -181,6 +182,10 @@ type VM struct { // block. acceptedBlockDB database.Database + // [warpDB] is used to store warp message signatures + // set to a prefixDB with the prefix [warpPrefix] + warpDB database.Database + toEngine chan<- commonEng.Message syntacticBlockValidator BlockValidator @@ -210,6 +215,10 @@ type VM struct { // State sync server and client StateSyncServer StateSyncClient + + // Avalanche Warp Messaging backend + // Used to serve BLS signatures of warp messages over RPC + warpBackend warp.WarpBackend } /* @@ -278,6 +287,17 @@ func (vm *VM) Initialize( vm.db = versiondb.New(baseDB) vm.acceptedBlockDB = prefixdb.New(acceptedPrefix, vm.db) vm.metadataDB = prefixdb.New(metadataPrefix, vm.db) + vm.warpDB = prefixdb.New(warpPrefix, vm.db) + + if vm.config.InspectDatabase { + start := time.Now() + log.Info("Starting database inspection") + if err := rawdb.InspectDatabase(vm.chaindb, nil, nil); err != nil { + return err + } + log.Info("Completed database inspection", "elapsed", time.Since(start)) + } + g := new(core.Genesis) if err := json.Unmarshal(genesisBytes, g); err != nil { return err @@ -350,6 +370,7 @@ func (vm *VM) Initialize( vm.ethConfig.CommitInterval = vm.config.CommitInterval vm.ethConfig.SkipUpgradeCheck = vm.config.SkipUpgradeCheck vm.ethConfig.AcceptedCacheSize = vm.config.AcceptedCacheSize + vm.ethConfig.TxLookupLimit = vm.config.TxLookupLimit // Create directory for offline pruning if len(vm.ethConfig.OfflinePruningDataDirectory) != 0 { @@ -372,6 +393,13 @@ func (vm *VM) Initialize( vm.chainConfig = g.Config vm.networkID = vm.ethConfig.NetworkId + if !vm.config.SkipSubnetEVMUpgradeCheck { + // check that subnetEVM upgrade is enabled from genesis before upgradeBytes + if !vm.chainConfig.IsSubnetEVM(common.Big0) { + return errSubnetEVMUpgradeNotEnabled + } + } + // Apply upgradeBytes (if any) by unmarshalling them into [chainConfig.UpgradeConfig]. // Initializing the chain will verify upgradeBytes are compatible with existing values. if len(upgradeBytes) > 0 { @@ -398,9 +426,12 @@ func (vm *VM) Initialize( // initialize peer network vm.networkCodec = message.Codec - vm.Network = peer.NewNetwork(appSender, vm.networkCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests) + vm.Network = peer.NewNetwork(appSender, vm.networkCodec, message.CrossChainCodec, chainCtx.NodeID, vm.config.MaxOutboundActiveRequests, vm.config.MaxOutboundActiveCrossChainRequests) vm.client = peer.NewNetworkClient(vm.Network) + // initialize warp backend + vm.warpBackend = warp.NewWarpBackend(vm.ctx, vm.warpDB, warpSignatureCacheSize) + if err := vm.initializeChain(lastAcceptedHash, vm.ethConfig); err != nil { return err } @@ -519,6 +550,7 @@ func (vm *VM) initializeStateSyncServer() { }) vm.setAppRequestHandlers() + vm.setCrossChainAppRequestHandler() } func (vm *VM) initChainState(lastAcceptedBlock *types.Block) error { @@ -590,13 +622,16 @@ func (vm *VM) setAppRequestHandlers() { Cache: vm.config.StateSyncServerTrieCache, }, ) - syncRequestHandler := handlers.NewSyncHandler( - vm.blockChain, - evmTrieDB, - vm.networkCodec, - handlerstats.NewHandlerStats(metrics.Enabled), - ) - vm.Network.SetRequestHandler(syncRequestHandler) + + networkHandler := newNetworkHandler(vm.blockChain, evmTrieDB, vm.networkCodec) + vm.Network.SetRequestHandler(networkHandler) +} + +// setCrossChainAppRequestHandler sets the request handlers for the VM to serve cross chain +// requests. +func (vm *VM) setCrossChainAppRequestHandler() { + crossChainRequestHandler := message.NewCrossChainHandler(vm.eth.APIBackend, message.CrossChainCodec) + vm.Network.SetCrossChainRequestHandler(crossChainRequestHandler) } // Shutdown implements the snowman.ChainVM interface @@ -777,6 +812,13 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]*commonEng.HTTPHandler enabledAPIs = append(enabledAPIs, "snowman") } + if vm.config.WarpAPIEnabled { + if err := handler.RegisterName("warp", &warp.WarpAPI{Backend: vm.warpBackend}); err != nil { + return nil, err + } + enabledAPIs = append(enabledAPIs, "warp") + } + log.Info(fmt.Sprintf("Enabled APIs: %s", strings.Join(enabledAPIs, ", "))) apis[ethRPCEndpoint] = &commonEng.HTTPHandler{ LockOptions: commonEng.NoLock, @@ -828,12 +870,6 @@ func (vm *VM) GetCurrentNonce(address common.Address) (uint64, error) { return state.GetNonce(address), nil } -// currentRules returns the chain rules for the current block. -func (vm *VM) currentRules() params.Rules { - header := vm.eth.APIBackend.CurrentHeader() - return vm.chainConfig.AvalancheRules(header.Number, big.NewInt(int64(header.Time))) -} - func (vm *VM) startContinuousProfiler() { // If the profiler directory is empty, return immediately // without creating or starting a continuous profiler. diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go index d97e23811f..6b6e3a14a8 100644 --- a/plugin/evm/vm_test.go +++ b/plugin/evm/vm_test.go @@ -18,7 +18,9 @@ import ( "time" "github.com/ava-labs/subnet-evm/commontype" + "github.com/ava-labs/subnet-evm/internal/ethapi" "github.com/ava-labs/subnet-evm/metrics" + "github.com/ava-labs/subnet-evm/plugin/evm/message" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" "github.com/ava-labs/subnet-evm/precompile/contracts/feemanager" @@ -27,6 +29,7 @@ import ( "github.com/ava-labs/subnet-evm/trie" "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -55,6 +58,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/rpc" + "github.com/ava-labs/subnet-evm/accounts/abi" accountKeystore "github.com/ava-labs/subnet-evm/accounts/keystore" ) @@ -70,9 +74,10 @@ var ( password = "CjasdjhiPeirbSenfeI13" // #nosec G101 // Use chainId: 43111, so that it does not overlap with any Avalanche ChainIDs, which may have their // config overridden in vm.Initialize. - genesisJSONSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"subnetEVMTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" - genesisJSONPreSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" - genesisJSONLatest = genesisJSONSubnetEVM + genesisJSONSubnetEVMLateEnablement = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"subnetEVMTimestamp\":50},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"subnetEVMTimestamp\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONPreSubnetEVM = "{\"config\":{\"chainId\":43111,\"homesteadBlock\":0,\"eip150Block\":0,\"eip150Hash\":\"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0\",\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x00\",\"gasLimit\":\"0x7A1200\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0x71562b71999873DB5b286dF957af199Ec94617F7\": {\"balance\":\"0x4192927743b88000\"}, \"0x703c4b2bD70c169f5717101CaeE543299Fc946C7\": {\"balance\":\"0x4192927743b88000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\"}" + genesisJSONLatest = genesisJSONSubnetEVM firstTxAmount *big.Int genesisBalance *big.Int @@ -380,6 +385,57 @@ func issueAndAccept(t *testing.T, issuer <-chan engCommon.Message, vm *VM) snowm return blk } +func TestSubnetEVMUpgradeRequiredAtGenesis(t *testing.T) { + genesisTests := []struct { + genesisJSON string + configJSON string + expectedErr error + }{ + { + // we expect an error when subnet evm upgrade is nil in chain config + genesisJSON: genesisJSONPreSubnetEVM, + configJSON: "", + expectedErr: errSubnetEVMUpgradeNotEnabled, + }, + { + // we expect an error when subnet evm upgrade is not enabled at genesis and at a later block instead + genesisJSON: genesisJSONSubnetEVMLateEnablement, + configJSON: "", + expectedErr: errSubnetEVMUpgradeNotEnabled, + }, + { + // we do not expect an err when skip-subnet-evm-upgrade-check is set to true + genesisJSON: genesisJSONPreSubnetEVM, + configJSON: "{\"skip-subnet-evm-upgrade-check\": true}", + expectedErr: nil, + }, + { + // we do not expect an err when skip-subnet-evm-upgrade-check is set to true + genesisJSON: genesisJSONSubnetEVMLateEnablement, + configJSON: "{\"skip-subnet-evm-upgrade-check\": true}", + expectedErr: nil, + }, + } + + for _, test := range genesisTests { + ctx, dbManager, genesisBytes, issuer := setupGenesis(t, test.genesisJSON) + vm := &VM{} + err := vm.Initialize( + context.Background(), + ctx, + dbManager, + genesisBytes, + []byte(""), + []byte(test.configJSON), + issuer, + []*engCommon.Fx{}, + nil, + ) + + require.ErrorIs(t, err, test.expectedErr) + } +} + func TestBuildEthTxBlock(t *testing.T) { // reduce block gas cost issuer, vm, dbManager, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "{\"pruning-enabled\":true}", "") @@ -2951,3 +3007,192 @@ func TestSkipChainConfigCheckCompatible(t *testing.T) { require.NoError(t, err) require.NoError(t, reinitVM.Shutdown(context.Background())) } + +func TestCrossChainMessagestoVM(t *testing.T) { + crossChainCodec := message.CrossChainCodec + require := require.New(t) + + // the following is based on this contract: + // contract T { + // event received(address sender, uint amount, bytes memo); + // event receivedAddr(address sender); + // + // function receive(bytes calldata memo) external payable returns (string memory res) { + // emit received(msg.sender, msg.value, memo); + // emit receivedAddr(msg.sender); + // return "hello world"; + // } + // } + + const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029` + const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]` + parsed, err := abi.JSON(strings.NewReader(abiJSON)) + require.NoErrorf(err, "could not parse abi: %v") + + calledSendCrossChainAppResponseFn := false + issuer, vm, _, appSender := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") + + defer func() { + err := vm.Shutdown(context.Background()) + require.NoError(err) + }() + + appSender.SendCrossChainAppResponseF = func(ctx context.Context, respondingChainID ids.ID, requestID uint32, responseBytes []byte) { + calledSendCrossChainAppResponseFn = true + + var response message.EthCallResponse + if _, err = crossChainCodec.Unmarshal(responseBytes, &response); err != nil { + require.NoErrorf(err, "unexpected error during unmarshal: %w") + } + + result := core.ExecutionResult{} + err = json.Unmarshal(response.ExecutionResult, &result) + require.NoError(err) + require.NotNil(result.ReturnData) + + finalResult, err := parsed.Unpack("receive", result.ReturnData) + require.NoError(err) + require.NotNil(finalResult) + require.Equal("hello world", finalResult[0]) + } + + newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1) + vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan) + + tx := types.NewTransaction(uint64(0), testEthAddrs[1], firstTxAmount, 21000, big.NewInt(testMinGasPrice), nil) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) + require.NoError(err) + + txErrors := vm.txPool.AddRemotesSync([]*types.Transaction{signedTx}) + for _, err := range txErrors { + require.NoError(err) + } + + <-issuer + + blk1, err := vm.BuildBlock(context.Background()) + require.NoError(err) + + err = blk1.Verify(context.Background()) + require.NoError(err) + + if status := blk1.Status(); status != choices.Processing { + t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) + } + + err = vm.SetPreference(context.Background(), blk1.ID()) + require.NoError(err) + + err = blk1.Accept(context.Background()) + require.NoError(err) + + newHead := <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk1.ID()) { + t.Fatalf("Expected new block to match") + } + + if status := blk1.Status(); status != choices.Accepted { + t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) + } + + lastAcceptedID, err := vm.LastAccepted(context.Background()) + require.NoError(err) + + if lastAcceptedID != blk1.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk1.ID(), lastAcceptedID) + } + + contractTx := types.NewContractCreation(1, common.Big0, 200000, big.NewInt(testMinGasPrice), common.FromHex(abiBin)) + contractSignedTx, err := types.SignTx(contractTx, types.NewEIP155Signer(vm.chainConfig.ChainID), testKeys[0]) + require.NoError(err) + + errs := vm.txPool.AddRemotesSync([]*types.Transaction{contractSignedTx}) + for _, err := range errs { + require.NoError(err) + } + testAddr := testEthAddrs[0] + contractAddress := crypto.CreateAddress(testAddr, 1) + + <-issuer + + blk2, err := vm.BuildBlock(context.Background()) + require.NoError(err) + + err = blk2.Verify(context.Background()) + require.NoError(err) + + if status := blk2.Status(); status != choices.Processing { + t.Fatalf("Expected status of built block to be %s, but found %s", choices.Processing, status) + } + + err = vm.SetPreference(context.Background(), blk2.ID()) + require.NoError(err) + + err = blk2.Accept(context.Background()) + require.NoError(err) + + newHead = <-newTxPoolHeadChan + if newHead.Head.Hash() != common.Hash(blk2.ID()) { + t.Fatalf("Expected new block to match") + } + + if status := blk2.Status(); status != choices.Accepted { + t.Fatalf("Expected status of accepted block to be %s, but found %s", choices.Accepted, status) + } + + lastAcceptedID, err = vm.LastAccepted(context.Background()) + require.NoError(err) + + if lastAcceptedID != blk2.ID() { + t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk2.ID(), lastAcceptedID) + } + + input, err := parsed.Pack("receive", []byte("X")) + require.NoError(err) + + data := hexutil.Bytes(input) + + requestArgs, err := json.Marshal(ðapi.TransactionArgs{ + To: &contractAddress, + Data: &data, + }) + require.NoError(err) + + var ethCallRequest message.CrossChainRequest = message.EthCallRequest{ + RequestArgs: requestArgs, + } + + crossChainRequest, err := crossChainCodec.Marshal(message.Version, ðCallRequest) + require.NoError(err) + + requestingChainID := ids.ID(common.BytesToHash([]byte{1, 2, 3, 4, 5})) + + // we need all items in the acceptor queue to be processed before we process a cross chain request + vm.blockChain.DrainAcceptorQueue() + err = vm.Network.CrossChainAppRequest(context.Background(), requestingChainID, 1, time.Now().Add(60*time.Second), crossChainRequest) + require.NoError(err) + require.True(calledSendCrossChainAppResponseFn, "sendCrossChainAppResponseFn was not called") +} + +func TestSignatureRequestsToVM(t *testing.T) { + _, vm, _, _ := GenesisVM(t, true, genesisJSONSubnetEVM, "", "") + + defer func() { + err := vm.Shutdown(context.Background()) + require.NoError(t, err) + }() + + // Generate a SignatureRequest for an unknown message + var signatureRequest message.Request = message.SignatureRequest{ + MessageID: ids.GenerateTestID(), + } + + requestBytes, err := message.Codec.Marshal(message.Version, &signatureRequest) + require.NoError(t, err) + + // Currently with warp not being initialized we just need to make sure the NoopSignatureRequestHandler does not + // panic/crash when sent a SignatureRequest. + // TODO: We will need to update the test when warp is initialized to check for expected response. + err = vm.Network.AppRequest(context.Background(), ids.GenerateTestNodeID(), 1, time.Now().Add(60*time.Second), requestBytes) + require.NoError(t, err) +} diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go index a4ac725a59..4880f0e348 100644 --- a/plugin/evm/vm_upgrade_bytes_test.go +++ b/plugin/evm/vm_upgrade_bytes_test.go @@ -165,9 +165,10 @@ func TestVMUpgradeBytesNetworkUpgrades(t *testing.T) { if err != nil { t.Fatalf("could not marshal upgradeConfig to json: %s", err) } + configJSON := "{\"skip-subnet-evm-upgrade-check\": true}" // initialize the VM with these upgrade bytes - issuer, vm, dbManager, appSender := GenesisVM(t, true, genesisJSONPreSubnetEVM, "", string(upgradeBytesJSON)) + issuer, vm, dbManager, appSender := GenesisVM(t, true, genesisJSONPreSubnetEVM, configJSON, string(upgradeBytesJSON)) vm.clock.Set(subnetEVMTimestamp) // verify upgrade is applied @@ -238,9 +239,10 @@ func TestVMUpgradeBytesNetworkUpgradesWithGenesis(t *testing.T) { if err != nil { t.Fatalf("could not marshal upgradeConfig to json: %s", err) } + configJSON := "{\"skip-subnet-evm-upgrade-check\": true}" // initialize the VM with these upgrade bytes - _, vm, _, _ := GenesisVM(t, true, string(genesisBytes), "", string(upgradeBytesJSON)) + _, vm, _, _ := GenesisVM(t, true, string(genesisBytes), configJSON, string(upgradeBytesJSON)) // verify upgrade is rescheduled assert.False(t, vm.chainConfig.IsSubnetEVM(genesisSubnetEVMTimestamp)) @@ -258,7 +260,7 @@ func TestVMUpgradeBytesNetworkUpgradesWithGenesis(t *testing.T) { } // initialize the VM with these upgrade bytes - _, vm, _, _ = GenesisVM(t, true, string(genesisBytes), "", string(upgradeBytesJSON)) + _, vm, _, _ = GenesisVM(t, true, string(genesisBytes), configJSON, string(upgradeBytesJSON)) // verify upgrade is aborted assert.False(t, vm.chainConfig.IsSubnetEVM(genesisSubnetEVMTimestamp)) diff --git a/plugin/main.go b/plugin/main.go index b37ebf3cfb..d8b4e772c2 100644 --- a/plugin/main.go +++ b/plugin/main.go @@ -9,19 +9,20 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/ulimit" + "github.com/ava-labs/avalanchego/version" "github.com/ava-labs/avalanchego/vms/rpcchainvm" "github.com/ava-labs/subnet-evm/plugin/evm" ) func main() { - version, err := PrintVersion() + printVersion, err := PrintVersion() if err != nil { fmt.Printf("couldn't get config: %s", err) os.Exit(1) } - if version { - fmt.Println(evm.Version) + if printVersion { + fmt.Printf("Subnet-EVM/%s [AvalancheGo=%s, rpcchainvm=%d]\n", evm.Version, version.Current, version.RPCChainVMProtocol) os.Exit(0) } if err := ulimit.Set(ulimit.DefaultFDLimit, logging.NoLog{}); err != nil { diff --git a/precompile/contracts/feemanager/config_test.go b/precompile/contracts/feemanager/config_test.go index 861c713023..7a21efdf46 100644 --- a/precompile/contracts/feemanager/config_test.go +++ b/precompile/contracts/feemanager/config_test.go @@ -39,12 +39,9 @@ func TestVerifyFeeManagerConfig(t *testing.T) { expectedError: "cannot set address", }, { - name: "invalid initial fee manager config", - config: NewConfig(big.NewInt(3), admins, nil, - &commontype.FeeConfig{ - GasLimit: big.NewInt(0), - }), - expectedError: "gasLimit = 0 cannot be less than or equal to 0", + name: "invalid initial fee manager config", + config: NewConfig(big.NewInt(3), admins, nil, &commontype.FeeConfig{}), + expectedError: "gasLimit cannot be nil", }, } for _, tt := range tests { diff --git a/scripts/build.sh b/scripts/build.sh index deb5713e0d..d6da71d6ed 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -40,14 +40,14 @@ source "$SUBNET_EVM_PATH"/scripts/versions.sh source "$SUBNET_EVM_PATH"/scripts/constants.sh if [[ $# -eq 1 ]]; then - binary_path=$1 + BINARY_PATH=$1 elif [[ $# -eq 0 ]]; then - binary_path="$GOPATH/src/github.com/ava-labs/avalanchego/build/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy" + BINARY_PATH="$GOPATH/src/github.com/ava-labs/avalanchego/build/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy" else echo "Invalid arguments to build subnet-evm. Requires zero (default location) or one argument to specify binary location." exit 1 fi # Build Subnet EVM, which is run as a subprocess -echo "Building Subnet EVM Version: $subnet_evm_version at $binary_path" -go build -ldflags "-X github.com/ava-labs/subnet-evm/plugin/evm.GitCommit=$subnet_evm_commit -X github.com/ava-labs/subnet-evm/plugin/evm.Version=$subnet_evm_version $static_ld_flags" -o "$binary_path" "plugin/"*.go +echo "Building Subnet EVM Version: $SUBNET_EVM_VERSION at $BINARY_PATH" +go build -ldflags "-X github.com/ava-labs/subnet-evm/plugin/evm.GitCommit=$SUBNET_EVM_COMMIT -X github.com/ava-labs/subnet-evm/plugin/evm.Version=$SUBNET_EVM_VERSION $STATIC_LD_FLAGS" -o "$BINARY_PATH" "plugin/"*.go diff --git a/scripts/build_image.sh b/scripts/build_image.sh index 06e2705b38..48c3b93117 100755 --- a/scripts/build_image.sh +++ b/scripts/build_image.sh @@ -13,8 +13,8 @@ source "$SUBNET_EVM_PATH"/scripts/versions.sh # Load the constants source "$SUBNET_EVM_PATH"/scripts/constants.sh -echo "Building Docker Image: $dockerhub_repo:$build_image_id based of $avalanche_version" -docker build -t "$dockerhub_repo:$build_image_id" "$SUBNET_EVM_PATH" -f "$SUBNET_EVM_PATH/Dockerfile" \ - --build-arg AVALANCHE_VERSION="$avalanche_version" \ - --build-arg SUBNET_EVM_COMMIT="$subnet_evm_commit" \ - --build-arg CURRENT_BRANCH="$current_branch" +echo "Building Docker Image: $DOCKERHUB_REPO:$BUILD_IMAGE_ID based of $AVALANCHEGO_VERSION" +docker build -t "$DOCKERHUB_REPO:$BUILD_IMAGE_ID" "$SUBNET_EVM_PATH" -f "$SUBNET_EVM_PATH/Dockerfile" \ + --build-arg AVALANCHE_VERSION="$AVALANCHEGO_VERSION" \ + --build-arg SUBNET_EVM_COMMIT="$SUBNET_EVM_COMMIT" \ + --build-arg CURRENT_BRANCH="$CURRENT_BRANCH" diff --git a/scripts/build_test.sh b/scripts/build_test.sh index bc8cd79fea..adb148a01e 100755 --- a/scripts/build_test.sh +++ b/scripts/build_test.sh @@ -20,5 +20,5 @@ source "$SUBNET_EVM_PATH"/scripts/constants.sh # We pass in the arguments to this script directly to enable easily passing parameters such as enabling race detection, # parallelism, and test coverage. -# DO NOT RUN "tests/e2e" since it's run by ginkgo -go test -coverprofile=coverage.out -covermode=atomic -timeout="30m" $@ $(go list ./... | grep -v tests/e2e) +# DO NOT RUN "tests/precompile" or "tests/load" since it's run by ginkgo +go test -coverprofile=coverage.out -covermode=atomic -timeout="30m" $@ $(go list ./... | grep -v tests/precompile | grep -v tests/load) diff --git a/scripts/constants.sh b/scripts/constants.sh index a58f910926..3a8aca35df 100644 --- a/scripts/constants.sh +++ b/scripts/constants.sh @@ -4,37 +4,37 @@ GOPATH="$(go env GOPATH)" # Avalabs docker hub -dockerhub_repo="avaplatform/avalanchego" +DOCKERHUB_REPO="avaplatform/avalanchego" # if this isn't a git repository (say building from a release), don't set our git constants. if [ ! -d .git ] then - current_branch="" - subnet_evm_commit="" - subnet_evm_commit_id="" + CURRENT_BRANCH="" + SUBNET_EVM_COMMIT="" + SUBNET_EVM_COMMIT_ID="" else # Current branch - current_branch=${CURRENT_BRANCH:-$(git describe --tags --exact-match 2> /dev/null || git symbolic-ref -q --short HEAD || git rev-parse --short HEAD || :)} + CURRENT_BRANCH=${CURRENT_BRANCH:-$(git describe --tags --exact-match 2> /dev/null || git symbolic-ref -q --short HEAD || git rev-parse --short HEAD || :)} # Image build id # # Use an abbreviated version of the full commit to tag the image. # WARNING: this will use the most recent commit even if there are un-committed changes present - subnet_evm_commit="$(git --git-dir="$SUBNET_EVM_PATH/.git" rev-parse HEAD || :)" - subnet_evm_commit_id="${subnet_evm_commit::8}" + SUBNET_EVM_COMMIT="$(git --git-dir="$SUBNET_EVM_PATH/.git" rev-parse HEAD || :)" + SUBNET_EVM_COMMIT_ID="${SUBNET_EVM_COMMIT::8}" fi -echo "Using branch: ${current_branch}" +echo "Using branch: ${CURRENT_BRANCH}" -build_image_id=${BUILD_IMAGE_ID:-"$avalanche_version-$subnet_evm_commit_id"} +BUILD_IMAGE_ID=${BUILD_IMAGE_ID:-"$AVALANCHEGO_VERSION-$SUBNET_EVM_COMMIT_ID"} # Static compilation -static_ld_flags='' +STATIC_LD_FLAGS='' if [ "${STATIC_COMPILATION:-}" = 1 ] then export CC=musl-gcc command -v $CC || ( echo $CC must be available for static compilation && exit 1 ) - static_ld_flags=' -extldflags "-static" -linkmode external ' + STATIC_LD_FLAGS=' -extldflags "-static" -linkmode external ' fi # Set the CGO flags to use the portable version of BLST diff --git a/scripts/generate_precompile.sh b/scripts/generate_precompile.sh new file mode 100755 index 0000000000..8876880659 --- /dev/null +++ b/scripts/generate_precompile.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -e + +# This script generates a Stateful Precompile stub based off of a Solidity ABI file. +# It first sets the necessary CGO_FLAGs for the BLST library used in AvalancheGo and +# then runs PrecompileGen. +if ! [[ "$0" =~ scripts/generate_precompile.sh ]]; then + echo "must be run from repository root, but got $0" + exit 255 +fi + +# Load the versions +SUBNET_EVM_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) + +# Load the constants +source "$SUBNET_EVM_PATH"/scripts/constants.sh + +go run ./cmd/precompilegen/main.go $@ diff --git a/scripts/install_avalanchego_release.sh b/scripts/install_avalanchego_release.sh new file mode 100755 index 0000000000..ad0fcc66bc --- /dev/null +++ b/scripts/install_avalanchego_release.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -e + +# Load the versions +SUBNET_EVM_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) +source "$SUBNET_EVM_PATH"/scripts/versions.sh + +# Load the constants +source "$SUBNET_EVM_PATH"/scripts/constants.sh + +VERSION=$AVALANCHEGO_VERSION + +############################ +# download avalanchego +# https://github.com/ava-labs/avalanchego/releases +GOARCH=$(go env GOARCH) +GOOS=$(go env GOOS) +BASEDIR=${BASE_DIR:-"/tmp/avalanchego-release"} +mkdir -p ${BASEDIR} +AVAGO_DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/${VERSION}/avalanchego-linux-${GOARCH}-${VERSION}.tar.gz +AVAGO_DOWNLOAD_PATH=${BASEDIR}/avalanchego-linux-${GOARCH}-${VERSION}.tar.gz +if [[ ${GOOS} == "darwin" ]]; then + AVAGO_DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/${VERSION}/avalanchego-macos-${VERSION}.zip + AVAGO_DOWNLOAD_PATH=${BASEDIR}/avalanchego-macos-${VERSION}.zip +fi + +AVALANCHEGO_BUILD_PATH=${AVALANCHEGO_BUILD_PATH:-${BASEDIR}/avalanchego-${VERSION}} +mkdir -p $AVALANCHEGO_BUILD_PATH + +if [[ ! -f ${AVAGO_DOWNLOAD_PATH} ]]; then + echo "downloading avalanchego ${VERSION} at ${AVAGO_DOWNLOAD_URL} to ${AVAGO_DOWNLOAD_PATH}" + curl -L ${AVAGO_DOWNLOAD_URL} -o ${AVAGO_DOWNLOAD_PATH} +fi +echo "extracting downloaded avalanchego to ${AVALANCHEGO_BUILD_PATH}" +if [[ ${GOOS} == "linux" ]]; then + mkdir -p ${AVALANCHEGO_BUILD_PATH} && tar xzvf ${AVAGO_DOWNLOAD_PATH} --directory ${AVALANCHEGO_BUILD_PATH} --strip-components 1 +elif [[ ${GOOS} == "darwin" ]]; then + unzip ${AVAGO_DOWNLOAD_PATH} -d ${AVALANCHEGO_BUILD_PATH} + mv ${AVALANCHEGO_BUILD_PATH}/build/* ${AVALANCHEGO_BUILD_PATH} + rm -rf ${AVALANCHEGO_BUILD_PATH}/build/ +fi + +AVALANCHEGO_PATH=${AVALANCHEGO_BUILD_PATH}/avalanchego +AVALANCHEGO_PLUGIN_DIR=${AVALANCHEGO_BUILD_PATH}/plugins + +echo "Installed AvalancheGo release ${VERSION}" +echo "AvalancheGo Path: ${AVALANCHEGO_PATH}" +echo "Plugin Dir: ${AVALANCHEGO_PLUGIN_DIR}" diff --git a/scripts/lint_allowed_geth_imports.sh b/scripts/lint_allowed_geth_imports.sh index cf1ba23e22..acd0f92003 100755 --- a/scripts/lint_allowed_geth_imports.sh +++ b/scripts/lint_allowed_geth_imports.sh @@ -8,7 +8,7 @@ set -o pipefail # 1. Recursively search through all go files for any lines that include a direct import from go-ethereum # 2. Sort the unique results # #. Print out the difference between the search results and the list of specified allowed package imports from geth. -extra_imports=$(grep -r --include='*.go' '"github.com/ethereum/go-ethereum/.*"' -o -h | sort -u | comm -23 - ./scripts/geth-allowed-packages.txt) +extra_imports=$(grep -r --include='*.go' --exclude-dir='simulator' '"github.com/ethereum/go-ethereum/.*"' -o -h | sort -u | comm -23 - ./scripts/geth-allowed-packages.txt) if [ ! -z "${extra_imports}" ]; then echo "new go-ethereum imports should be added to ./scripts/geth-allowed-packages.txt to prevent accidental imports:" echo "${extra_imports}" diff --git a/scripts/run.sh b/scripts/run.sh index 5e851297aa..699a7f4588 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,18 +1,10 @@ #!/usr/bin/env bash set -e -# e.g., -# -# run without e2e tests -# ./scripts/run.sh -# -# run without e2e tests, and with simulator -# RUN_SIMULATOR=true ./scripts/run.sh -# -# run without e2e tests with DEBUG log level -# AVALANCHE_LOG_LEVEL=DEBUG ./scripts/run.sh +# This script starts N nodes (TODO N instead of 5) and waits for ctrl-c to shutdown the process group of AvalancheGo processes +# Uses data directory to store all AvalancheGo data neatly in one location with minimal config overhead if ! [[ "$0" =~ scripts/run.sh ]]; then - echo "must be run from repository root" + echo "must be run from repository root, but got $0" exit 255 fi @@ -26,243 +18,38 @@ source "$SUBNET_EVM_PATH"/scripts/versions.sh # Load the constants source "$SUBNET_EVM_PATH"/scripts/constants.sh -VERSION=$avalanche_version - -# "ewoq" key -DEFAULT_ACCOUNT="0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC" -GENESIS_ADDRESS=${GENESIS_ADDRESS-$DEFAULT_ACCOUNT} - -SKIP_NETWORK_RUNNER_START=${SKIP_NETWORK_RUNNER_START:-false} -SKIP_NETWORK_RUNNER_SHUTDOWN=${SKIP_NETWORK_RUNNER_SHUTDOWN:-true} -RUN_SIMULATOR=${RUN_SIMULATOR:-false} -ENABLE_SOLIDITY_TESTS=${ENABLE_SOLIDITY_TESTS:-false} -AVALANCHE_LOG_LEVEL=${AVALANCHE_LOG_LEVEL:-WARN} -ANR_VERSION=$network_runner_version -GINKGO_VERSION=$ginkgo_version - -# by default, "run.sh" should not run any tests... -# simulator tests are not implemented in ginkgo -# it instead runs external binary "simulator" -# so we exclude all e2e tests here -# ref. https://onsi.github.io/ginkgo/#spec-labels -GINKGO_LABEL_FILTER="!precompile-upgrade && !solidity-with-npx && !solidity-counter" -if [[ ${RUN_SIMULATOR} == true ]]; then - # only run "ping" tests, no other test - # because simulator itself will generate loads and run tests - GINKGO_LABEL_FILTER="ping" -fi -if [[ ${ENABLE_SOLIDITY_TESTS} == true ]]; then - GINKGO_LABEL_FILTER="solidity-with-npx" -fi - -echo "Running with:" -echo AVALANCHE_VERSION: ${VERSION} -echo ANR_VERSION: ${ANR_VERSION} -echo GINKGO_VERSION: ${GINKGO_VERSION} -echo GENESIS_ADDRESS: ${GENESIS_ADDRESS} -echo SKIP_NETWORK_RUNNER_START: ${SKIP_NETWORK_RUNNER_START} -echo SKIP_NETWORK_RUNNER_SHUTDOWN: ${SKIP_NETWORK_RUNNER_SHUTDOWN} -echo RUN_SIMULATOR: ${RUN_SIMULATOR} -echo ENABLE_SOLIDITY_TESTS: ${ENABLE_SOLIDITY_TESTS} -echo GINKGO_LABEL_FILTER: ${GINKGO_LABEL_FILTER} -echo AVALANCHE_LOG_LEVEL: ${AVALANCHE_LOG_LEVEL} - -############################ -# download avalanchego -# https://github.com/ava-labs/avalanchego/releases -GOARCH=$(go env GOARCH) -GOOS=$(go env GOOS) -BASEDIR=/tmp/subnet-evm-runner -mkdir -p ${BASEDIR} -AVAGO_DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/${VERSION}/avalanchego-linux-${GOARCH}-${VERSION}.tar.gz -AVAGO_DOWNLOAD_PATH=${BASEDIR}/avalanchego-linux-${GOARCH}-${VERSION}.tar.gz -if [[ ${GOOS} == "darwin" ]]; then - AVAGO_DOWNLOAD_URL=https://github.com/ava-labs/avalanchego/releases/download/${VERSION}/avalanchego-macos-${VERSION}.zip - AVAGO_DOWNLOAD_PATH=${BASEDIR}/avalanchego-macos-${VERSION}.zip -fi - -AVAGO_FILEPATH=${BASEDIR}/avalanchego-${VERSION} -if [[ ! -d ${AVAGO_FILEPATH} ]]; then - if [[ ! -f ${AVAGO_DOWNLOAD_PATH} ]]; then - echo "downloading avalanchego ${VERSION} at ${AVAGO_DOWNLOAD_URL} to ${AVAGO_DOWNLOAD_PATH}" - curl -L ${AVAGO_DOWNLOAD_URL} -o ${AVAGO_DOWNLOAD_PATH} - fi - echo "extracting downloaded avalanchego to ${AVAGO_FILEPATH}" - if [[ ${GOOS} == "linux" ]]; then - mkdir -p ${AVAGO_FILEPATH} && tar xzvf ${AVAGO_DOWNLOAD_PATH} --directory ${AVAGO_FILEPATH} --strip-components 1 - elif [[ ${GOOS} == "darwin" ]]; then - unzip ${AVAGO_DOWNLOAD_PATH} -d ${AVAGO_FILEPATH} - mv ${AVAGO_FILEPATH}/build/* ${AVAGO_FILEPATH} - rm -rf ${AVAGO_FILEPATH}/build/ - fi - find ${BASEDIR}/avalanchego-${VERSION} -fi - -AVALANCHEGO_PATH=${AVAGO_FILEPATH}/avalanchego -AVALANCHEGO_PLUGIN_DIR=${AVAGO_FILEPATH}/plugins - -################################# -# compile subnet-evm -# Check if SUBNET_EVM_COMMIT is set, if not retrieve the last commit from the repo. -# This is used in the Dockerfile to allow a commit hash to be passed in without -# including the .git/ directory within the Docker image. -subnet_evm_commit=${SUBNET_EVM_COMMIT:-$(git rev-list -1 HEAD)} - -# Build Subnet EVM, which is run as a subprocess -echo "Building Subnet EVM Version: $subnet_evm_version; GitCommit: $subnet_evm_commit" -go build \ - -ldflags "-X github.com/ava-labs/subnet_evm/plugin/evm.GitCommit=$subnet_evm_commit -X github.com/ava-labs/subnet_evm/plugin/evm.Version=$subnet_evm_version" \ - -o $AVALANCHEGO_PLUGIN_DIR/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy \ - "plugin/"*.go - -################################# -# write subnet-evm genesis - -# Create genesis file to use in network (make sure to add your address to -# "alloc") -export CHAIN_ID=99999 -echo "creating genesis" - cat <$BASEDIR/genesis.json -{ - "config": { - "chainId": $CHAIN_ID, - "homesteadBlock": 0, - "eip150Block": 0, - "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "muirGlacierBlock": 0, - "subnetEVMTimestamp": 0, - "feeConfig": { - "gasLimit": 20000000, - "minBaseFee": 1000000000, - "targetGas": 100000000, - "baseFeeChangeDenominator": 48, - "minBlockGasCost": 0, - "maxBlockGasCost": 10000000, - "targetBlockRate": 2, - "blockGasCostStep": 500000 - } - }, - "alloc": { - "${GENESIS_ADDRESS:2}": { - "balance": "0x52B7D2DCC80CD2E4000000" - } - }, - "nonce": "0x0", - "timestamp": "0x0", - "extraData": "0x00", - "gasLimit": "0x1312D00", - "difficulty": "0x0", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" -} +# Set up avalanche binary path and assume build directory is set +AVALANCHEGO_BUILD_PATH=${AVALANCHEGO_BUILD_PATH:-"$GOPATH/src/github.com/ava-labs/avalanchego/build"} +AVALANCHEGO_PATH=${AVALANCHEGO_PATH:-"$AVALANCHEGO_BUILD_PATH/avalanchego"} +AVALANCHEGO_PLUGIN_DIR=${AVALANCHEGO_PLUGIN_DIR:-"$AVALANCHEGO_BUILD_PATH/plugins"} +DATA_DIR=${DATA_DIR:-/tmp/subnet-evm-start-node/$(date "+%Y-%m-%d%:%H:%M:%S")} + +mkdir -p $DATA_DIR + +# Set the config file contents for the path passed in as the first argument +function _set_config(){ + cat <$1 + { + "network-id": "local", + "staking-enabled": false, + "health-check-frequency": "5s", + "plugin-dir": "$AVALANCHEGO_PLUGIN_DIR" + } EOF - -################################# -# download avalanche-network-runner -# https://github.com/ava-labs/avalanche-network-runner -ANR_REPO_PATH=github.com/ava-labs/avalanche-network-runner -# version set -go install -v ${ANR_REPO_PATH}@${ANR_VERSION} - -################################# -# run "avalanche-network-runner" server -GOPATH=$(go env GOPATH) -if [[ -z ${GOBIN+x} ]]; then - # no gobin set - BIN=${GOPATH}/bin/avalanche-network-runner -else - # gobin set - BIN=${GOBIN}/avalanche-network-runner -fi -echo "launch avalanche-network-runner in the background" -$BIN server \ - --log-level debug \ - --port=":12342" \ - --grpc-gateway-port=":12343" & -PID=${!} - -run_ginkgo() { - echo "building e2e.test" - # to install the ginkgo binary (required for test build and run) - go install -v github.com/onsi/ginkgo/v2/ginkgo@${GINKGO_VERSION} - ginkgo -h - - ACK_GINKGO_RC=true ginkgo build ./tests/e2e - - # By default, it runs all e2e test cases! - # Use "--ginkgo.skip" to skip tests. - # Use "--ginkgo.focus" to select tests. - echo "running e2e tests with SKIP_NETWORK_RUNNER_START ${SKIP_NETWORK_RUNNER_START}" - ./tests/e2e/e2e.test \ - --ginkgo.vv \ - --network-runner-log-level debug \ - --network-runner-grpc-endpoint="0.0.0.0:12342" \ - --avalanchego-path=${AVALANCHEGO_PATH} \ - --avalanchego-plugin-dir=${AVALANCHEGO_PLUGIN_DIR} \ - --avalanchego-log-level=${AVALANCHE_LOG_LEVEL} \ - --vm-genesis-path=$BASEDIR/genesis.json \ - --output-path=$BASEDIR/avalanchego-${VERSION}/output.yaml \ - --skip-network-runner-start=${SKIP_NETWORK_RUNNER_START} \ - --skip-network-runner-shutdown=${SKIP_NETWORK_RUNNER_SHUTDOWN} \ - --ginkgo.label-filter="${GINKGO_LABEL_FILTER}" } -run_simulator() { - ################################# - echo "building simulator" - pushd ./cmd/simulator - go install -v . - popd - - echo "running simulator" - simulator \ - --cluster-info-yaml=$BASEDIR/avalanchego-${VERSION}/output.yaml \ - --keys=./cmd/simulator/.simulator/keys \ - --timeout=30s \ - --concurrency=10 \ - --base-fee=25 \ - --priority-fee=1 +function execute_cmd() { + echo "Executing command: $@" + $@ } +NODE_NAME="node1" +NODE_DATA_DIR="$DATA_DIR/$NODE_NAME" +echo "Creating data directory: $NODE_DATA_DIR" +mkdir -p $NODE_DATA_DIR +NODE_CONFIG_FILE_PATH="$NODE_DATA_DIR/config.json" +_set_config $NODE_CONFIG_FILE_PATH -# whether start network via parser/main.go or e2e.test ginkgo -# run the tests with label filter -echo "running ginkgo" -run_ginkgo -# to fail the script if ginkgo failed -EXIT_CODE=$? - - -# e.g., "RUN_SIMULATOR=true scripts/run.sh" to launch network runner + simulator -if [[ ${RUN_SIMULATOR} == true ]]; then - run_simulator - # to fail the script if simulator failed - EXIT_CODE=$? -fi - -################################# -if [[ ${SKIP_NETWORK_RUNNER_SHUTDOWN} == false ]]; then - # just in case tests are aborted, manually terminate them again - echo "network-runner RPC server was running on PID ${PID} as test mode; terminating the process..." - pkill -P ${PID} || true - kill -2 ${PID} - pkill -9 -f srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy || true # in case pkill didn't work -else - echo "network-runner RPC server is running on PID ${PID}..." - echo "" - echo "use the following command to terminate:" - echo "" - echo "pkill -P ${PID} && kill -2 ${PID} && pkill -9 -f srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy" - echo "" -fi +CMD="$AVALANCHEGO_PATH --data-dir=$NODE_DATA_DIR --config-file=$NODE_CONFIG_FILE_PATH" -exit ${EXIT_CODE} +execute_cmd $CMD diff --git a/scripts/run_ginkgo.sh b/scripts/run_ginkgo.sh new file mode 100755 index 0000000000..db9bd61bf2 --- /dev/null +++ b/scripts/run_ginkgo.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -e + +# This script assumes that an AvalancheGo and Subnet-EVM binaries are available in the standard location +# within the $GOPATH +# The AvalancheGo and PluginDir paths can be specified via the environment variables used in ./scripts/run.sh. + +# Load the versions +SUBNET_EVM_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) + +source "$SUBNET_EVM_PATH"/scripts/constants.sh + +source "$SUBNET_EVM_PATH"/scripts/versions.sh + +# Build ginkgo +echo "building precompile.test" +# to install the ginkgo binary (required for test build and run) +go install -v github.com/onsi/ginkgo/v2/ginkgo@${GINKGO_VERSION} + +ACK_GINKGO_RC=true ginkgo build ./tests/precompile ./tests/load + +# By default, it runs all e2e test cases! +# Use "--ginkgo.skip" to skip tests. +# Use "--ginkgo.focus" to select tests. +./tests/precompile/precompile.test \ + --ginkgo.vv \ + --ginkgo.label-filter=${GINKGO_LABEL_FILTER:-""} + +./tests/load/load.test \ + --ginkgo.vv \ + --ginkgo.label-filter=${GINKGO_LABEL_FILTER:-""} diff --git a/scripts/run_simulator.sh b/scripts/run_simulator.sh new file mode 100755 index 0000000000..8cd218df0b --- /dev/null +++ b/scripts/run_simulator.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -e + +echo "Beginning simulator script" + +if ! [[ "$0" =~ scripts/run_simulator.sh ]]; then + echo "must be run from repository root, but got $0" + exit 255 +fi + +# Load the versions +SUBNET_EVM_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) +source "$SUBNET_EVM_PATH"/scripts/versions.sh + +# Load the constants +source "$SUBNET_EVM_PATH"/scripts/constants.sh + +run_simulator() { + ################################# + echo "building simulator" + pushd ./cmd/simulator + go install -v . + echo + + popd + echo "running simulator from $PWD" + simulator \ + --rpc-endpoints=$RPC_ENDPOINTS \ + --keys=./cmd/simulator/.simulator/keys \ + --timeout=30s \ + --concurrency=10 \ + --base-fee=300 \ + --priority-fee=100 +} + +run_simulator diff --git a/scripts/versions.sh b/scripts/versions.sh index d0b6c05a22..708a8c549a 100644 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -1,11 +1,10 @@ #!/usr/bin/env bash -# Set up the versions to be used -subnet_evm_version=${SUBNET_EVM_VERSION:-'v0.4.7'} +# Set up the versions to be used - populate ENV variables only if they are not already populated +SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-'v0.4.9'} # Don't export them as they're used in the context of other calls -avalanche_version=${AVALANCHE_VERSION:-'v1.9.5'} -network_runner_version=${NETWORK_RUNNER_VERSION:-'35be10cd3867a94fbe960a1c14a455f179de60d9'} -ginkgo_version=${GINKGO_VERSION:-'v2.2.0'} +AVALANCHEGO_VERSION=${AVALANCHE_VERSION:-'v1.9.8'} +GINKGO_VERSION=${GINKGO_VERSION:-'v2.2.0'} # This won't be used, but it's here to make code syncs easier -latest_coreth_version=0.11.3 +LATEST_CORETH_VERSION=0.11.6 diff --git a/sync/client/client.go b/sync/client/client.go index d4cde79bc4..773baf54c5 100644 --- a/sync/client/client.go +++ b/sync/client/client.go @@ -325,14 +325,14 @@ func (c *client) get(ctx context.Context, request message.Request, parseFn parse start time.Time = time.Now() ) if len(c.stateSyncNodes) == 0 { - response, nodeID, err = c.networkClient.RequestAny(StateSyncVersion, requestBytes) + response, nodeID, err = c.networkClient.SendAppRequestAny(StateSyncVersion, requestBytes) } else { // get the next nodeID using the nodeIdx offset. If we're out of nodes, loop back to 0 // we do this every attempt to ensure we get a different node each time if possible. nodeIdx := atomic.AddUint32(&c.stateSyncNodeIdx, 1) nodeID = c.stateSyncNodes[nodeIdx%uint32(len(c.stateSyncNodes))] - response, err = c.networkClient.Request(nodeID, requestBytes) + response, err = c.networkClient.SendAppRequest(nodeID, requestBytes) } metric.UpdateRequestLatency(time.Since(start)) diff --git a/sync/client/mock_network.go b/sync/client/mock_network.go index 16981e4471..b9729350fa 100644 --- a/sync/client/mock_network.go +++ b/sync/client/mock_network.go @@ -28,7 +28,7 @@ type mockNetwork struct { nodesRequested []ids.NodeID } -func (t *mockNetwork) RequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) { +func (t *mockNetwork) SendAppRequestAny(minVersion *version.Application, request []byte) ([]byte, ids.NodeID, error) { if len(t.response) == 0 { return nil, ids.EmptyNodeID, errors.New("no mocked response to return in mockNetwork") } @@ -39,7 +39,7 @@ func (t *mockNetwork) RequestAny(minVersion *version.Application, request []byte return response, ids.EmptyNodeID, err } -func (t *mockNetwork) Request(nodeID ids.NodeID, request []byte) ([]byte, error) { +func (t *mockNetwork) SendAppRequest(nodeID ids.NodeID, request []byte) ([]byte, error) { if len(t.response) == 0 { return nil, errors.New("no mocked response to return in mockNetwork") } @@ -77,6 +77,10 @@ func (t *mockNetwork) Gossip([]byte) error { panic("not implemented") // we don't care about this function for this test } +func (t *mockNetwork) SendCrossChainRequest(chainID ids.ID, request []byte) ([]byte, error) { + panic("not implemented") // we don't care about this function for this test +} + func (t *mockNetwork) mockResponse(times uint8, callback func(), response []byte) { t.response = make([][]byte, times) for i := uint8(0); i < times; i++ { diff --git a/sync/handlers/code_request_test.go b/sync/handlers/code_request_test.go index cbf6b277bb..a7094742e1 100644 --- a/sync/handlers/code_request_test.go +++ b/sync/handlers/code_request_test.go @@ -94,7 +94,7 @@ func TestCodeRequestHandler(t *testing.T) { responseBytes, err := codeRequestHandler.OnCodeRequest(context.Background(), ids.GenerateTestNodeID(), 1, request) assert.NoError(t, err) - // If the expected resposne is empty, assert that the handler returns an empty response and return early. + // If the expected response is empty, assert that the handler returns an empty response and return early. if len(expectedResponse) == 0 { assert.Len(t, responseBytes, 0, "expected response to be empty") return diff --git a/sync/handlers/handler.go b/sync/handlers/handler.go index 5c0389cf40..867941aa83 100644 --- a/sync/handlers/handler.go +++ b/sync/handlers/handler.go @@ -4,20 +4,11 @@ package handlers import ( - "context" - - "github.com/ava-labs/avalanchego/codec" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/subnet-evm/core/state/snapshot" "github.com/ava-labs/subnet-evm/core/types" - "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ava-labs/subnet-evm/sync/handlers/stats" - "github.com/ava-labs/subnet-evm/trie" "github.com/ethereum/go-ethereum/common" ) -var _ message.RequestHandler = &syncHandler{} - type BlockProvider interface { GetBlock(common.Hash, uint64) *types.Block } @@ -30,35 +21,3 @@ type SyncDataProvider interface { BlockProvider SnapshotProvider } - -type syncHandler struct { - stateTrieLeafsRequestHandler *LeafsRequestHandler - blockRequestHandler *BlockRequestHandler - codeRequestHandler *CodeRequestHandler -} - -// NewSyncHandler constructs the handler for serving state sync. -func NewSyncHandler( - provider SyncDataProvider, - evmTrieDB *trie.Database, - networkCodec codec.Manager, - stats stats.HandlerStats, -) message.RequestHandler { - return &syncHandler{ - stateTrieLeafsRequestHandler: NewLeafsRequestHandler(evmTrieDB, provider, networkCodec, stats), - blockRequestHandler: NewBlockRequestHandler(provider, networkCodec, stats), - codeRequestHandler: NewCodeRequestHandler(evmTrieDB.DiskDB(), networkCodec, stats), - } -} - -func (s *syncHandler) HandleTrieLeafsRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, leafsRequest message.LeafsRequest) ([]byte, error) { - return s.stateTrieLeafsRequestHandler.OnLeafsRequest(ctx, nodeID, requestID, leafsRequest) -} - -func (s *syncHandler) HandleBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest message.BlockRequest) ([]byte, error) { - return s.blockRequestHandler.OnBlockRequest(ctx, nodeID, requestID, blockRequest) -} - -func (s *syncHandler) HandleCodeRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, codeRequest message.CodeRequest) ([]byte, error) { - return s.codeRequestHandler.OnCodeRequest(ctx, nodeID, requestID, codeRequest) -} diff --git a/tests/curl.go b/tests/curl.go deleted file mode 100644 index e51e074c1e..0000000000 --- a/tests/curl.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -// -// This file is a derived work, based on github.com/etcd-io/etcd/pkg/expect. - -package tests - -import ( - "bufio" - "fmt" - "os" - "os/exec" - "strings" - "sync" - "syscall" - "time" - - "github.com/creack/pty" -) - -// Sends a POST request to the endpoint with JSON body "dataRaw", -// and returns an error if "expect" string is not found in the response. -func CURLPost(endpoint string, dataRaw string, expect string) (string, error) { - cmdArgs := []string{ - "curl", - "-L", endpoint, - "-m", "10", - "-H", "Content-Type: application/json", - "-X", "POST", - "-d", dataRaw, - } - - ex, err := newExpect(cmdArgs[0], cmdArgs[1:]...) - if err != nil { - return "", err - } - defer ex.Close() - - return ex.Expect(expect) -} - -const DEBUG_LINES_TAIL = 40 - -type expectProcess struct { - cmd *exec.Cmd - fpty *os.File - wg sync.WaitGroup - - mu sync.RWMutex // protects lines and err - lines []string - err error - - stopSignal os.Signal -} - -func newExpect(name string, args ...string) (ep *expectProcess, err error) { - cmd := exec.Command(name, args...) - ep = &expectProcess{ - cmd: cmd, - stopSignal: syscall.SIGKILL, - } - ep.cmd.Stderr = ep.cmd.Stdout - ep.cmd.Stdin = nil - - if ep.fpty, err = pty.Start(ep.cmd); err != nil { - return nil, err - } - - ep.wg.Add(1) - go ep.read() - return ep, nil -} - -func (ep *expectProcess) read() { - defer ep.wg.Done() - printDebugLines := os.Getenv("EXPECT_DEBUG") != "" - r := bufio.NewReader(ep.fpty) - for { - l, err := r.ReadString('\n') - ep.mu.Lock() - if l != "" { - if printDebugLines { - fmt.Printf("%s-%d: %s", ep.cmd.Path, ep.cmd.Process.Pid, l) - } - ep.lines = append(ep.lines, l) - } - if err != nil { - ep.err = err - ep.mu.Unlock() - break - } - ep.mu.Unlock() - } -} - -func (ep *expectProcess) ExpectFunc(f func(string) bool) (string, error) { - i := 0 - - for { - ep.mu.Lock() - for i < len(ep.lines) { - line := ep.lines[i] - i++ - if f(line) { - ep.mu.Unlock() - return line, nil - } - } - if ep.err != nil { - ep.mu.Unlock() - break - } - ep.mu.Unlock() - time.Sleep(time.Millisecond * 100) - } - ep.mu.Lock() - lastLinesIndex := len(ep.lines) - DEBUG_LINES_TAIL - if lastLinesIndex < 0 { - lastLinesIndex = 0 - } - lastLines := strings.Join(ep.lines[lastLinesIndex:], "") - ep.mu.Unlock() - return "", fmt.Errorf("match not found."+ - " Set EXPECT_DEBUG for more info Err: %v, last lines:\n%s", - ep.err, lastLines) -} - -// Expect returns the first line containing the given string. -func (ep *expectProcess) Expect(s string) (string, error) { - return ep.ExpectFunc(func(txt string) bool { return strings.Contains(txt, s) }) -} - -// Stop kills the expect process and waits for it to exit. -func (ep *expectProcess) Stop() error { return ep.close(true) } - -// Signal sends a signal to the expect process -func (ep *expectProcess) Signal(sig os.Signal) error { - return ep.cmd.Process.Signal(sig) -} - -func (ep *expectProcess) Close() error { return ep.close(false) } - -func (ep *expectProcess) close(kill bool) error { - if ep.cmd == nil { - return ep.err - } - if kill { - ep.Signal(ep.stopSignal) - } - - err := ep.cmd.Wait() - ep.fpty.Close() - ep.wg.Wait() - - if err != nil { - if !kill && strings.Contains(err.Error(), "exit status") { - // non-zero exit code - err = nil - } else if kill && strings.Contains(err.Error(), "signal:") { - err = nil - } - } - - ep.cmd = nil - return err -} - -func (ep *expectProcess) ProcessError() error { - if strings.Contains(ep.err.Error(), "input/output error") { - // TODO: The expect library should not return - // `/dev/ptmx: input/output error` when process just exits. - return nil - } - return ep.err -} - -func (ep *expectProcess) Lines() []string { - ep.mu.RLock() - defer ep.mu.RUnlock() - return ep.lines -} diff --git a/tests/curl_test.go b/tests/curl_test.go deleted file mode 100644 index 1324d3b985..0000000000 --- a/tests/curl_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tests - -import "testing" - -func TestExpectFunc(t *testing.T) { - ep, err := newExpect("echo", "hello world") - if err != nil { - t.Fatal(err) - } - wstr := "hello world\r\n" - l, eerr := ep.ExpectFunc(func(a string) bool { return len(a) > 10 }) - if eerr != nil { - t.Fatal(eerr) - } - if l != wstr { - t.Fatalf(`got "%v", expected "%v"`, l, wstr) - } - if cerr := ep.Close(); cerr != nil { - t.Fatal(cerr) - } -} - -func TestEcho(t *testing.T) { - ep, err := newExpect("echo", "hello world") - if err != nil { - t.Fatal(err) - } - l, eerr := ep.Expect("world") - if eerr != nil { - t.Fatal(eerr) - } - wstr := "hello world" - if l[:len(wstr)] != wstr { - t.Fatalf(`got "%v", expected "%v"`, l, wstr) - } - if cerr := ep.Close(); cerr != nil { - t.Fatal(cerr) - } - if _, eerr = ep.Expect("..."); eerr == nil { - t.Fatalf("expected error on closed expect process") - } -} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go deleted file mode 100644 index 7ebe67e567..0000000000 --- a/tests/e2e/e2e_test.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// e2e implements the e2e tests. -package e2e - -import ( - "context" - "flag" - "fmt" - "os" - "testing" - "time" - - runner_sdk "github.com/ava-labs/avalanche-network-runner-sdk" - runner_sdk_rpcpb "github.com/ava-labs/avalanche-network-runner-sdk/rpcpb" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/tests/e2e/utils" - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - _ "github.com/ava-labs/subnet-evm/tests/e2e/ping" - _ "github.com/ava-labs/subnet-evm/tests/e2e/solidity" -) - -func TestE2E(t *testing.T) { - gomega.RegisterFailHandler(ginkgo.Fail) - ginkgo.RunSpecs(t, "subnet-evm e2e test suites") -} - -type networkClient struct { - client runner_sdk.Client -} - -var ( - networkRunnerLogLevel string - gRPCEp string - gRPCGatewayEp string - - // sets the "avalanchego" exec path - avalanchegoExecPath string - avalanchegoPluginDir string - avalanchegoLogLevel string - vmGenesisPath string - - outputFile string - - skipNetworkRunnerStart bool - skipNetworkRunnerShutdown bool -) - -func init() { - flag.StringVar( - &networkRunnerLogLevel, - "network-runner-log-level", - "info", - "gRPC server endpoint", - ) - flag.StringVar( - &gRPCEp, - "network-runner-grpc-endpoint", - "0.0.0.0:8080", - "gRPC server endpoint", - ) - flag.StringVar( - &gRPCGatewayEp, - "network-runner-grpc-gateway-endpoint", - "0.0.0.0:8081", - "gRPC gateway endpoint", - ) - - flag.StringVar( - &avalanchegoExecPath, - "avalanchego-path", - "", - "avalanchego executable path", - ) - flag.StringVar( - &avalanchegoPluginDir, - "avalanchego-plugin-dir", - "", - "avalanchego plugin directory", - ) - flag.StringVar( - &avalanchegoLogLevel, - "avalanchego-log-level", - "info", - "avalanchego log level", - ) - flag.StringVar( - &vmGenesisPath, - "vm-genesis-path", - "", - "VM genesis file path", - ) - flag.StringVar( - &outputFile, - "output-path", - "", - "output YAML path to write local cluster information", - ) - - flag.BoolVar( - &skipNetworkRunnerStart, - "skip-network-runner-start", - false, - "'true' to skip network runner start", - ) - flag.BoolVar( - &skipNetworkRunnerShutdown, - "skip-network-runner-shutdown", - false, - "'true' to skip network runner shutdown", - ) -} - -const vmName = "subnetevm" - -var vmID ids.ID - -func init() { - // TODO: add "getVMID" util function in avalanchego and import from "avalanchego" - b := make([]byte, 32) - copy(b, []byte(vmName)) - var err error - vmID, err = ids.ToID(b) - if err != nil { - panic(err) - } -} - -var subnetEVMRPCEps []string - -var _ = ginkgo.BeforeSuite(func() { - networkClient := createNetworkClient() - - if skipNetworkRunnerStart { - utils.Outf("{{green}}skipped 'start'{{/}}\n") - return - } - - networkClient.startNetwork() - networkClient.checkHealth() -}) - -func createNetworkClient() *networkClient { - runnerCli, err := runner_sdk.New(runner_sdk.Config{ - LogLevel: networkRunnerLogLevel, - Endpoint: gRPCEp, - DialTimeout: 10 * time.Second, - }) - gomega.Expect(err).Should(gomega.BeNil()) - - utils.SetOutputFile(outputFile) - utils.SetExecPath(avalanchegoExecPath) - utils.SetPluginDir(avalanchegoPluginDir) - utils.SetVmGenesisPath(vmGenesisPath) - utils.SetSkipNetworkRunnerShutdown(skipNetworkRunnerShutdown) - utils.SetClient(runnerCli) - - // Set AVALANCHEGO_PATH for the solidity suite - os.Setenv("AVALANCHEGO_PATH", avalanchegoExecPath) - - return &networkClient{client: runnerCli} -} - -func (n *networkClient) startNetwork() { - utils.Outf("{{green}}sending 'start' with binary path:{{/}} %q\n", utils.GetExecPath()) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - resp, err := n.client.Start( - ctx, - utils.GetExecPath(), - runner_sdk.WithPluginDir(utils.GetPluginDir()), - runner_sdk.WithGlobalNodeConfig(fmt.Sprintf(`{"log-level":"%s"}`, avalanchegoLogLevel)), - runner_sdk.WithNumNodes(5), - runner_sdk.WithBlockchainSpecs( - []*runner_sdk_rpcpb.BlockchainSpec{ - { - VmName: vmName, - Genesis: utils.GetVmGenesisPath(), - }, - }, - )) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - utils.Outf("{{green}}successfully started:{{/}} %+v\n", resp.ClusterInfo.NodeNames) -} - -func (n *networkClient) checkHealth() { - // TODO: network runner health should imply custom VM healthiness - // or provide a separate API for custom VM healthiness - // "start" is async, so wait some time for cluster health - utils.Outf("\n{{magenta}}sleeping before checking custom VM status...{{/}}\n") - time.Sleep(2 * time.Minute) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := n.client.Health(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - - subnetEVMRPCEps = make([]string, 0) - blockchainID, logsDir := "", "" - pid := 0 - - // wait up to 5-minute for custom VM installation - utils.Outf("\n{{magenta}}waiting for all custom VMs to report healthy...{{/}}\n") - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Minute) -done: - for ctx.Err() == nil { - select { - case <-ctx.Done(): - break done - case <-time.After(5 * time.Second): - } - - utils.Outf("{{magenta}}checking custom VM status{{/}}\n") - cctx, ccancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := n.client.Status(cctx) - ccancel() - gomega.Expect(err).Should(gomega.BeNil()) - - // all logs are stored under root data dir - logsDir = resp.GetClusterInfo().GetRootDataDir() - - // ANR server pid - pid = int(resp.GetClusterInfo().GetPid()) - - for blkChainID, vmInfo := range resp.ClusterInfo.CustomChains { - if vmInfo.VmId == vmID.String() { - blockchainID = blkChainID - utils.Outf("{{blue}}subnet-evm is ready:{{/}} %+v\n", vmInfo) - break done - } - } - } - gomega.Expect(ctx.Err()).Should(gomega.BeNil()) - cancel() - - gomega.Expect(blockchainID).Should(gomega.Not(gomega.BeEmpty())) - gomega.Expect(logsDir).Should(gomega.Not(gomega.BeEmpty())) - - cctx, ccancel := context.WithTimeout(context.Background(), 2*time.Minute) - uris, err := n.client.URIs(cctx) - ccancel() - gomega.Expect(err).Should(gomega.BeNil()) - utils.Outf("{{blue}}avalanche HTTP RPCs URIs:{{/}} %q\n", uris) - - for _, u := range uris { - rpcEP := fmt.Sprintf("%s/ext/bc/%s/rpc", u, blockchainID) - subnetEVMRPCEps = append(subnetEVMRPCEps, rpcEP) - utils.Outf("{{blue}}avalanche subnet-evm RPC:{{/}} %q\n", rpcEP) - } - - utils.Outf("{{blue}}{{bold}}writing output %q with PID %d{{/}}\n", utils.GetOutputPath(), pid) - ci := utils.ClusterInfo{ - URIs: uris, - Endpoint: fmt.Sprintf("/ext/bc/%s", blockchainID), - PID: pid, - LogsDir: logsDir, - SubnetEVMRPCEndpoints: subnetEVMRPCEps, - } - utils.SetClusterInfo(ci) - gomega.Expect(ci.Save(utils.GetOutputPath())).Should(gomega.BeNil()) - - b, err := os.ReadFile(utils.GetOutputPath()) - gomega.Expect(err).Should(gomega.BeNil()) - utils.Outf("\n{{blue}}$ cat %s:{{/}}\n%s\n", utils.GetOutputPath(), string(b)) -} - -var _ = ginkgo.AfterSuite(func() { - if utils.GetSkipNetworkRunnerShutdown() { - return - } - - // if cluster is running, shut it down - if isRunnerUp() { - gomega.Expect(stopNetwork()).Should(gomega.BeNil()) - } - gomega.Expect(closeClient()).Should(gomega.BeNil()) -}) - -func isRunnerUp() bool { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - _, err := utils.GetClient().Health(ctx) - cancel() - return err == nil -} - -func stopNetwork() error { - utils.Outf("{{red}}shutting down network{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err := utils.GetClient().Stop(ctx) - cancel() - return err -} - -func closeClient() error { - utils.Outf("{{red}}shutting down client{{/}}\n") - return utils.GetClient().Close() -} diff --git a/tests/e2e/ping/suites.go b/tests/e2e/ping/suites.go deleted file mode 100644 index 62cd7a6e61..0000000000 --- a/tests/e2e/ping/suites.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// Implements ping tests, requires network-runner cluster. -package ping - -import ( - "context" - "time" - - "github.com/ava-labs/subnet-evm/tests/e2e/utils" - - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -var _ = utils.DescribeLocal("[Ping]", func() { - ginkgo.It("can ping network-runner RPC server", ginkgo.Label("ping"), func() { - runnerCli := utils.GetClient() - gomega.Expect(runnerCli).ShouldNot(gomega.BeNil()) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - _, err := runnerCli.Ping(ctx) - cancel() - gomega.Expect(err).Should(gomega.BeNil()) - }) -}) diff --git a/tests/e2e/runner/runner.go b/tests/e2e/runner/runner.go deleted file mode 100644 index 377814b71d..0000000000 --- a/tests/e2e/runner/runner.go +++ /dev/null @@ -1,226 +0,0 @@ -package runner - -import ( - "context" - "errors" - "fmt" - "os" - "time" - - client "github.com/ava-labs/avalanche-network-runner-sdk" - "github.com/ava-labs/avalanche-network-runner-sdk/rpcpb" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/tests/e2e/utils" - - "sigs.k8s.io/yaml" -) - -type clusterInfo struct { - URIs []string `json:"uris"` - Endpoint string `json:"endpoint"` - PID int `json:"pid"` - LogsDir string `json:"logsDir"` -} - -const fsModeWrite = 0o600 - -func (ci clusterInfo) Save(p string) error { - ob, err := yaml.Marshal(ci) - if err != nil { - return err - } - return os.WriteFile(p, ob, fsModeWrite) -} - -func startRunner(grpcEp string, execPath string, vmName string, genesisPath string, pluginDir string) error { - cli, err := client.New(client.Config{ - LogLevel: "info", - Endpoint: grpcEp, - DialTimeout: 10 * time.Second, - }) - if err != nil { - return err - } - - utils.Outf("{{green}}tests/e2e/runner sending 'start' with binary path:{{/}} %q\n", execPath) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) - resp, err := cli.Start( - ctx, - execPath, - client.WithPluginDir(pluginDir), - client.WithBlockchainSpecs([]*rpcpb.BlockchainSpec{ - { - VmName: vmName, - Genesis: genesisPath, - }, - }), - ) - cancel() - if err != nil { - return err - } - utils.Outf("{{green}}successfully started:{{/}} %+v\n", resp.ClusterInfo.NodeNames) - return nil -} - -func WaitForCustomVm(grpcEp string, vmId ids.ID) (string, string, int, error) { - cli, err := client.New(client.Config{ - LogLevel: "info", - Endpoint: grpcEp, - DialTimeout: 10 * time.Second, - }) - if err != nil { - return "", "", 0, err - } - - blockchainID, logsDir := "", "" - pid := 0 - - // wait up to 5-minute for custom VM installation - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) -done: - for ctx.Err() == nil { - select { - case <-ctx.Done(): - break done - case <-time.After(5 * time.Second): - } - - utils.Outf("{{magenta}}checking custom VM status{{/}}\n") - cctx, ccancel := context.WithTimeout(context.Background(), 2*time.Minute) - resp, err := cli.Health(cctx) - ccancel() - if err != nil { - cancel() - return "", "", 0, err - } - - if !resp.ClusterInfo.Healthy { - continue - } - - if !resp.ClusterInfo.CustomChainsHealthy { - continue - } - - // all logs are stored under root data dir - logsDir = resp.GetClusterInfo().GetRootDataDir() - - // ANR server pid - pid = int(resp.GetClusterInfo().GetPid()) - - for chainID, chainInfo := range resp.ClusterInfo.CustomChains { - if chainInfo.VmId == vmId.String() { - blockchainID = chainID - utils.Outf("{{blue}}subnet-evm is ready:{{/}} %+v\n", chainInfo) - break done - } - } - } - err = ctx.Err() - if err != nil { - cancel() - return "", "", 0, err - } - cancel() - - if blockchainID == "" { - return "", "", 0, errors.New("BlockchainId not found") - } - if logsDir == "" { - return "", "", 0, errors.New("logsDir not found") - } - if pid == 0 { - return "", "", pid, errors.New("pid not found") - } - return blockchainID, logsDir, pid, nil -} - -func SaveClusterInfo(grpcEp string, blockchainId string, logsDir string, pid int) (clusterInfo, error) { - cli, err := client.New(client.Config{ - LogLevel: "info", - Endpoint: grpcEp, - DialTimeout: 10 * time.Second, - }) - if err != nil { - return clusterInfo{}, err - } - - cctx, ccancel := context.WithTimeout(context.Background(), 2*time.Minute) - uris, err := cli.URIs(cctx) - ccancel() - if err != nil { - return clusterInfo{}, err - } - utils.Outf("{{blue}}avalanche HTTP RPCs URIs:{{/}} %q\n", uris) - - subnetEVMRPCEps := make([]string, 0) - for _, u := range uris { - rpcEP := fmt.Sprintf("%s/ext/bc/%s/rpc", u, blockchainId) - subnetEVMRPCEps = append(subnetEVMRPCEps, rpcEP) - utils.Outf("{{blue}}avalanche subnet-evm RPC:{{/}} %q\n", rpcEP) - } - - ci := clusterInfo{ - URIs: uris, - Endpoint: fmt.Sprintf("/ext/bc/%s", blockchainId), - PID: pid, - LogsDir: logsDir, - } - err = ci.Save(utils.GetOutputPath()) - if err != nil { - return clusterInfo{}, err - } - return ci, nil -} - -func StartNetwork(grpcEp string, execPath string, vmId ids.ID, vmName string, genesisPath string, pluginDir string) (clusterInfo, error) { - fmt.Println("Starting network") - startRunner(grpcEp, execPath, vmName, genesisPath, pluginDir) - - blockchainId, logsDir, pid, err := WaitForCustomVm(grpcEp, vmId) - if err != nil { - return clusterInfo{}, err - } - fmt.Println("Got custom vm") - - return SaveClusterInfo(grpcEp, blockchainId, logsDir, pid) -} - -func StopNetwork(grpcEp string) error { - cli, err := client.New(client.Config{ - LogLevel: "info", - Endpoint: grpcEp, - DialTimeout: 10 * time.Second, - }) - if err != nil { - return err - } - - utils.Outf("{{red}}shutting down network{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - _, err = cli.Stop(ctx) - cancel() - return err -} - -func ShutdownClient() error { - utils.Outf("{{red}}shutting down client{{/}}\n") - return nil -} - -func IsRunnerUp(grpcEp string) bool { - cli, err := client.New(client.Config{ - LogLevel: "info", - Endpoint: grpcEp, - DialTimeout: 10 * time.Second, - }) - if err != nil { - return false - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - _, err = cli.Health(ctx) - cancel() - return err == nil -} diff --git a/tests/e2e/solidity/suites.go b/tests/e2e/solidity/suites.go deleted file mode 100644 index 0d641112ff..0000000000 --- a/tests/e2e/solidity/suites.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -// Implements solidity tests. -package solidity - -import ( - "fmt" - "os" - "os/exec" - - "github.com/ava-labs/subnet-evm/plugin/evm" - "github.com/ava-labs/subnet-evm/tests/e2e/runner" - "github.com/ava-labs/subnet-evm/tests/e2e/utils" - - ginkgo "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" -) - -const vmName = "subnetevm" - -// network-runner-grpc-endpoint from run script -const grpcEp = "0.0.0.0:12342" - -func runHardhatTests(test string) { - cmd := exec.Command("npx", "hardhat", "test", test, "--network", "e2e") - cmd.Dir = "./contract-examples" - out, err := cmd.Output() - if err != nil { - fmt.Println(string(out)) - fmt.Println(err) - } - gomega.Expect(err).Should(gomega.BeNil()) -} - -// startSubnet starts a test network and launches a subnetEVM instance with the genesis file at [genesisPath] -func startSubnet(genesisPath string) error { - fmt.Println("AVALANCHEGO_PATH:", os.Getenv("AVALANCHEGO_PATH")) - _, err := runner.StartNetwork(grpcEp, os.Getenv("AVALANCHEGO_PATH"), evm.ID, vmName, genesisPath, utils.GetPluginDir()) - gomega.Expect(err).Should(gomega.BeNil()) - return utils.UpdateHardhatConfig() -} - -// stopSubnet stops the test network. -func stopSubnet() { - err := runner.StopNetwork(grpcEp) - gomega.Expect(err).Should(gomega.BeNil()) -} - -var _ = utils.DescribePrecompile(func() { - ginkgo.It("tx allow list", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/tx_allow_list.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/ExampleTxAllowList.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - - ginkgo.It("deployer allow list", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/deployer_allow_list.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/ExampleDeployerList.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - - ginkgo.It("contract native minter", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/contract_native_minter.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/ERC20NativeMinter.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - - ginkgo.It("fee manager", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/fee_manager.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/ExampleFeeManager.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - - ginkgo.It("reward manager", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/reward_manager.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/ExampleRewardManager.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - - // ADD YOUR PRECOMPILE HERE - /* - ginkgo.It("your precompile", ginkgo.Label("solidity-with-npx"), func() { - err := startSubnet("./tests/e2e/genesis/{your_precompile}.json") - gomega.Expect(err).Should(gomega.BeNil()) - running := runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeTrue()) - runHardhatTests("./test/{YourPrecompileTest}.ts") - stopSubnet() - running = runner.IsRunnerUp(grpcEp) - gomega.Expect(running).Should(gomega.BeFalse()) - }) - */ -}) diff --git a/tests/e2e/utils/describe.go b/tests/e2e/utils/describe.go deleted file mode 100644 index cde6ee371c..0000000000 --- a/tests/e2e/utils/describe.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - ginkgo "github.com/onsi/ginkgo/v2" -) - -// DescribeLocal annotates the tests that requires local network-runner. -// Can only run with local cluster. -func DescribeLocal(text string, body func()) bool { - return ginkgo.Describe("[Local] "+text, body) -} - -func DescribePrecompile(body func()) bool { - return ginkgo.Describe("[Precompiles]", ginkgo.Ordered, body) -} diff --git a/tests/e2e/utils/out.go b/tests/e2e/utils/out.go deleted file mode 100644 index df718c40bc..0000000000 --- a/tests/e2e/utils/out.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "fmt" - - "github.com/onsi/ginkgo/v2/formatter" -) - -// Outputs to stdout. -// -// e.g., -// Outf("{{green}}{{bold}}hi there %q{{/}}", "aa") -// Outf("{{magenta}}{{bold}}hi therea{{/}} {{cyan}}{{underline}}b{{/}}") -// -// ref. -// https://github.com/onsi/ginkgo/blob/v2.0.0/formatter/formatter.go#L52-L73 -// -func Outf(format string, args ...interface{}) { - s := formatter.F(format, args...) - fmt.Fprint(formatter.ColorableStdOut, s) -} diff --git a/tests/e2e/utils/params.go b/tests/e2e/utils/params.go deleted file mode 100644 index 1a2b935c18..0000000000 --- a/tests/e2e/utils/params.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "os" - "sync" - - runner_sdk "github.com/ava-labs/avalanche-network-runner-sdk" - - "gopkg.in/yaml.v2" -) - -// ClusterInfo represents the local cluster information. -type ClusterInfo struct { - URIs []string `json:"uris"` - Endpoint string `json:"endpoint"` - PID int `json:"pid"` - LogsDir string `json:"logsDir"` - SubnetEVMRPCEndpoints []string `json:"subnetEVMRPCEndpoints"` -} - -const fsModeWrite = 0o600 - -func (ci ClusterInfo) Save(p string) error { - ob, err := yaml.Marshal(ci) - if err != nil { - return err - } - return os.WriteFile(p, ob, fsModeWrite) -} - -var ( - mu sync.RWMutex - - cli runner_sdk.Client - - outputFile string - pluginDir string - - // executable path for "avalanchego" - execPath string - vmGenesisPath string - - skipNetworkRunnerShutdown bool - - clusterInfo ClusterInfo -) - -func SetClient(c runner_sdk.Client) { - mu.Lock() - cli = c - mu.Unlock() -} - -func GetClient() runner_sdk.Client { - mu.RLock() - c := cli - mu.RUnlock() - return c -} - -func SetOutputFile(filepath string) { - mu.Lock() - outputFile = filepath - mu.Unlock() -} - -func GetOutputPath() string { - mu.RLock() - e := outputFile - mu.RUnlock() - return e -} - -// Sets the executable path for "avalanchego". -func SetExecPath(p string) { - mu.Lock() - execPath = p - mu.Unlock() -} - -// Loads the executable path for "avalanchego". -func GetExecPath() string { - mu.RLock() - e := execPath - mu.RUnlock() - return e -} - -func SetPluginDir(dir string) { - mu.Lock() - pluginDir = dir - mu.Unlock() -} - -func GetPluginDir() string { - mu.RLock() - p := pluginDir - mu.RUnlock() - return p -} - -func SetVmGenesisPath(p string) { - mu.Lock() - vmGenesisPath = p - mu.Unlock() -} - -func GetVmGenesisPath() string { - mu.RLock() - p := vmGenesisPath - mu.RUnlock() - return p -} - -func SetSkipNetworkRunnerShutdown(b bool) { - mu.Lock() - skipNetworkRunnerShutdown = b - mu.Unlock() -} - -func GetSkipNetworkRunnerShutdown() bool { - mu.RLock() - b := skipNetworkRunnerShutdown - mu.RUnlock() - return b -} - -func SetClusterInfo(c ClusterInfo) { - mu.Lock() - clusterInfo = c - mu.Unlock() -} - -func GetClusterInfo() ClusterInfo { - mu.RLock() - c := clusterInfo - mu.RUnlock() - return c -} diff --git a/tests/e2e/utils/parser.go b/tests/e2e/utils/parser.go deleted file mode 100644 index 103114b8a8..0000000000 --- a/tests/e2e/utils/parser.go +++ /dev/null @@ -1,80 +0,0 @@ -// (c) 2022 Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "encoding/json" - "fmt" - "io/ioutil" - - "github.com/fatih/color" - "gopkg.in/yaml.v2" -) - -/* -===Example File=== - -endpoint: /ext/bc/2Z36RnQuk1hvsnFeGWzfZUfXNr7w1SjzmDQ78YxfTVNAkDq3nZ -logsDir: /var/folders/mp/6jm81gc11dv3xtcwxmrd8mcr0000gn/T/runnerlogs2984620995 -pid: 55547 -uris: -- http://localhost:61278 -- http://localhost:61280 -- http://localhost:61282 -- http://localhost:61284 -- http://localhost:61286 -*/ - -type output struct { - Endpoint string `yaml:"endpoint"` - Logs string `yaml:"logsDir"` - PID int `yaml:"pid"` - URIs []string `yaml:"uris"` -} - -type rpcFile struct { - Rpc string `json:"rpc"` -} - -const ( - DYNAMIC_RPC_FILE = "contract-examples/dynamic_rpc.json" - LOCAL_RPC_FILE = "contract-examples/local_rpc.json" -) - -func writeRPC(rpcUrl string) error { - rpcFileData := rpcFile{ - Rpc: rpcUrl, - } - - file, err := json.MarshalIndent(rpcFileData, "", " ") - if err != nil { - return err - } - - err = ioutil.WriteFile(DYNAMIC_RPC_FILE, file, 0644) - if err != nil { - return err - } - err = ioutil.WriteFile(LOCAL_RPC_FILE, file, 0644) - return err -} - -func UpdateHardhatConfig() error { - yamlFile, err := ioutil.ReadFile(outputFile) - if err != nil { - return err - } - var o output - if err := yaml.Unmarshal(yamlFile, &o); err != nil { - return err - } - - color.Yellow("Updating hardhat config with RPC URL: %s%s/rpc", o.URIs[0], o.Endpoint) - - rpc := fmt.Sprintf("%s%s/rpc", o.URIs[0], o.Endpoint) - if err = writeRPC(rpc); err != nil { - return err - } - return nil -} diff --git a/tests/load/genesis/genesis.json b/tests/load/genesis/genesis.json new file mode 100644 index 0000000000..cac224d1a2 --- /dev/null +++ b/tests/load/genesis/genesis.json @@ -0,0 +1,45 @@ +{ + "config": { + "chainId": 99999, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "subnetEVMTimestamp": 0, + "feeConfig": { + "gasLimit": 20000000, + "minBaseFee": 1000000000, + "targetGas": 100000000, + "baseFeeChangeDenominator": 48, + "minBlockGasCost": 0, + "maxBlockGasCost": 10000000, + "targetBlockRate": 2, + "blockGasCostStep": 500000 + } + }, + "alloc": { + "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { + "balance": "0x52B7D2DCC80CD2E4000000" + }, + "0x0Fa8EA536Be85F32724D57A37758761B86416123": { + "balance": "0x52B7D2DCC80CD2E4000000" + } + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x1312D00", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + \ No newline at end of file diff --git a/tests/load/load_test.go b/tests/load/load_test.go new file mode 100644 index 0000000000..07641a4ce6 --- /dev/null +++ b/tests/load/load_test.go @@ -0,0 +1,79 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "fmt" + "os" + "os/exec" + "strings" + "testing" + "time" + + "github.com/ava-labs/avalanchego/api/health" + "github.com/ava-labs/subnet-evm/tests/utils" + "github.com/ethereum/go-ethereum/log" + "github.com/go-cmd/cmd" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var startCmd *cmd.Cmd + +func TestE2E(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "subnet-evm small load simulator test suite") +} + +// BeforeSuite starts an AvalancheGo process to use for the e2e tests +var _ = ginkgo.BeforeSuite(func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + wd, err := os.Getwd() + gomega.Expect(err).Should(gomega.BeNil()) + log.Info("Starting AvalancheGo node", "wd", wd) + startCmd, err = utils.RunCommand("./scripts/run.sh") + gomega.Expect(err).Should(gomega.BeNil()) + + // Assumes that startCmd will launch a node with HTTP Port at [utils.DefaultLocalNodeURI] + healthClient := health.NewClient(utils.DefaultLocalNodeURI) + healthy, err := health.AwaitReady(ctx, healthClient, 5*time.Second) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(healthy).Should(gomega.BeTrue()) + log.Info("AvalancheGo node is healthy") +}) + +var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { + ginkgo.It("basic subnet load test", ginkgo.Label("load"), func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + blockchainID := utils.CreateNewSubnet(ctx, "./tests/load/genesis/genesis.json") + + rpcEndpoints := make([]string, 0, len(utils.NodeURIs)) + for _, uri := range []string{utils.DefaultLocalNodeURI} { // TODO: use NodeURIs instead, hack until fixing multi node in a network behavior + rpcEndpoints = append(rpcEndpoints, fmt.Sprintf("%s/ext/bc/%s/rpc", uri, blockchainID)) + } + commaSeparatedRPCEndpoints := strings.Join(rpcEndpoints, ",") + err := os.Setenv("RPC_ENDPOINTS", commaSeparatedRPCEndpoints) + gomega.Expect(err).Should(gomega.BeNil()) + + log.Info("Sleeping with network running", "rpcEndpoints", commaSeparatedRPCEndpoints) + cmd := exec.Command("./scripts/run_simulator.sh") + log.Info("Running load simulator script", "cmd", cmd.String()) + + out, err := cmd.CombinedOutput() + fmt.Printf("\nCombined output:\n\n%s\n", string(out)) + gomega.Expect(err).Should(gomega.BeNil()) + }) +}) + +var _ = ginkgo.AfterSuite(func() { + gomega.Expect(startCmd).ShouldNot(gomega.BeNil()) + gomega.Expect(startCmd.Stop()).Should(gomega.BeNil()) + // TODO add a new node to bootstrap off of the existing node and ensure it can bootstrap all subnets + // created during the test +}) diff --git a/tests/e2e/genesis/deployer_allow_list.json b/tests/precompile/genesis/contract_deployer_allow_list.json similarity index 100% rename from tests/e2e/genesis/deployer_allow_list.json rename to tests/precompile/genesis/contract_deployer_allow_list.json diff --git a/tests/e2e/genesis/contract_native_minter.json b/tests/precompile/genesis/contract_native_minter.json similarity index 100% rename from tests/e2e/genesis/contract_native_minter.json rename to tests/precompile/genesis/contract_native_minter.json diff --git a/tests/e2e/genesis/fee_manager.json b/tests/precompile/genesis/fee_manager.json similarity index 100% rename from tests/e2e/genesis/fee_manager.json rename to tests/precompile/genesis/fee_manager.json diff --git a/tests/e2e/genesis/reward_manager.json b/tests/precompile/genesis/reward_manager.json similarity index 100% rename from tests/e2e/genesis/reward_manager.json rename to tests/precompile/genesis/reward_manager.json diff --git a/tests/e2e/genesis/tx_allow_list.json b/tests/precompile/genesis/tx_allow_list.json similarity index 100% rename from tests/e2e/genesis/tx_allow_list.json rename to tests/precompile/genesis/tx_allow_list.json diff --git a/tests/precompile/precompile_test.go b/tests/precompile/precompile_test.go new file mode 100644 index 0000000000..46acc862df --- /dev/null +++ b/tests/precompile/precompile_test.go @@ -0,0 +1,54 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package precompile + +import ( + "context" + "os" + "testing" + "time" + + "github.com/ava-labs/avalanchego/api/health" + "github.com/ava-labs/subnet-evm/tests/utils" + "github.com/ethereum/go-ethereum/log" + "github.com/go-cmd/cmd" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + // Import the solidity package, so that ginkgo maps out the tests declared within the package + _ "github.com/ava-labs/subnet-evm/tests/precompile/solidity" +) + +var startCmd *cmd.Cmd + +func TestE2E(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "subnet-evm precompile ginkgo test suite") +} + +// BeforeSuite starts an AvalancheGo process to use for the e2e tests +var _ = ginkgo.BeforeSuite(func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + wd, err := os.Getwd() + gomega.Expect(err).Should(gomega.BeNil()) + log.Info("Starting AvalancheGo node", "wd", wd) + startCmd, err = utils.RunCommand("./scripts/run.sh") + gomega.Expect(err).Should(gomega.BeNil()) + + // Assumes that startCmd will launch a node with HTTP Port at [utils.DefaultLocalNodeURI] + healthClient := health.NewClient(utils.DefaultLocalNodeURI) + healthy, err := health.AwaitReady(ctx, healthClient, 5*time.Second) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(healthy).Should(gomega.BeTrue()) + log.Info("AvalancheGo node is healthy") +}) + +var _ = ginkgo.AfterSuite(func() { + gomega.Expect(startCmd).ShouldNot(gomega.BeNil()) + gomega.Expect(startCmd.Stop()).Should(gomega.BeNil()) + // TODO add a new node to bootstrap off of the existing node and ensure it can bootstrap all subnets + // created during the test +}) diff --git a/tests/precompile/solidity/suites.go b/tests/precompile/solidity/suites.go new file mode 100644 index 0000000000..1404a01603 --- /dev/null +++ b/tests/precompile/solidity/suites.go @@ -0,0 +1,77 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Implements solidity tests. +package solidity + +import ( + "context" + "time" + + "github.com/ava-labs/avalanchego/api/health" + "github.com/ava-labs/subnet-evm/tests/utils" + ginkgo "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe("[Precompiles]", ginkgo.Ordered, func() { + ginkgo.It("ping the network", ginkgo.Label("setup"), func() { + client := health.NewClient(utils.DefaultLocalNodeURI) + healthy, err := client.Readiness(context.Background()) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(healthy.Healthy).Should(gomega.BeTrue()) + }) +}) + +var _ = ginkgo.Describe("[Precompiles]", ginkgo.Ordered, func() { + // Each ginkgo It node specifies the name of the genesis file (in ./tests/precompile/genesis/) + // to use to launch the subnet and the name of the TS test file to run on the subnet (in ./contract-examples/tests/) + ginkgo.It("contract native minter", ginkgo.Label("Precompile"), ginkgo.Label("ContractNativeMinter"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "contract_native_minter") + }) + + ginkgo.It("tx allow list", ginkgo.Label("Precompile"), ginkgo.Label("TxAllowList"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "tx_allow_list") + }) + + ginkgo.It("contract deployer allow list", ginkgo.Label("Precompile"), ginkgo.Label("ContractDeployerAllowList"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "contract_deployer_allow_list") + }) + + ginkgo.It("fee manager", ginkgo.Label("Precompile"), ginkgo.Label("FeeManager"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "fee_manager") + }) + + ginkgo.It("reward manager", ginkgo.Label("Precompile"), ginkgo.Label("RewardManager"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "reward_manager") + }) + + // TODO: can we refactor this so that it automagically checks to ensure each hardhat test file matches the name of a hardhat genesis file + // and then runs the hardhat tests for each one without forcing precompile developers to modify this file. + // ADD YOUR PRECOMPILE HERE + /* + ginkgo.It("your precompile", ginkgo.Label("Precompile"), ginkgo.Label("YourPrecompile"), func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Specify the name shared by the genesis file in ./tests/precompile/genesis/{your_precompile}.json + // and the test file in ./contract-examples/tests/{your_precompile}.ts + utils.ExecuteHardHatTestOnNewBlockchain(ctx, "your_precompile") + }) + */ +}) diff --git a/tests/e2e/utils/command.go b/tests/utils/command.go similarity index 56% rename from tests/e2e/utils/command.go rename to tests/utils/command.go index 4f6cb6fcf5..367547be0b 100644 --- a/tests/e2e/utils/command.go +++ b/tests/utils/command.go @@ -4,19 +4,22 @@ package utils import ( - "context" "fmt" "strings" "time" + "github.com/ethereum/go-ethereum/log" "github.com/go-cmd/cmd" ) -func RunCommand(ctx context.Context, bin string, args ...string) (cmd.Status, error) { - Outf("{{green}}running '%s %s'{{/}}\n", bin, strings.Join(args, " ")) +// RunCommand starts the command [bin] with the given [args] and returns the command to the caller +// TODO cmd package mentions we can do this more efficiently with cmd.NewCmdOptions rather than looping +// and calling Status(). +func RunCommand(bin string, args ...string) (*cmd.Cmd, error) { + log.Info("Executing", "cmd", fmt.Sprintf("%s %s", bin, strings.Join(args, " "))) curCmd := cmd.NewCmd(bin, args...) - statusChan := curCmd.Start() + _ = curCmd.Start() // to stream outputs ticker := time.NewTicker(10 * time.Millisecond) @@ -38,11 +41,5 @@ func RunCommand(ctx context.Context, bin string, args ...string) (cmd.Status, er } }() - select { - case s := <-statusChan: - return s, nil - case <-ctx.Done(): - curCmd.Stop() - return cmd.Status{}, ctx.Err() - } + return curCmd, nil } diff --git a/tests/utils/constants.go b/tests/utils/constants.go new file mode 100644 index 0000000000..75cc779bd3 --- /dev/null +++ b/tests/utils/constants.go @@ -0,0 +1,9 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +var ( + DefaultLocalNodeURI = "http://127.0.0.1:9650" + NodeURIs = []string{DefaultLocalNodeURI, "http://127.0.0.1:9652", "http://127.0.0.1:9654", "http://127.0.0.1:9656", "http://127.0.0.1:9658"} +) diff --git a/tests/e2e/utils/evm_client.go b/tests/utils/evm_client.go similarity index 85% rename from tests/e2e/utils/evm_client.go rename to tests/utils/evm_client.go index e1b9eecd12..8017aac1f5 100644 --- a/tests/e2e/utils/evm_client.go +++ b/tests/utils/evm_client.go @@ -9,13 +9,11 @@ import ( "fmt" "log" "math/big" - "strings" "time" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/vmerrs" "github.com/ethereum/go-ethereum/common" ) @@ -38,8 +36,9 @@ func NewEvmClient(ep string, baseFee uint64, priorityFee uint64) (*EvmClient, er } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + chainID, err := ethCli.ChainID(ctx) - cancel() if err != nil { return nil, err } @@ -113,6 +112,7 @@ func (ec *EvmClient) ConfirmTx(ctx context.Context, txHash common.Hash) (*big.In time.Sleep(time.Second) continue } + // XXX: this uses gas instead of gas used, so it may be incorrect if the transaction does more than a simple transfer return result.Cost(), nil } return nil, ctx.Err() @@ -128,18 +128,14 @@ func (ec *EvmClient) TransferTx( for ctx.Err() == nil { senderBal, err := ec.FetchBalance(ctx, sender) if err != nil { - log.Printf("could not get balance: %s", err.Error()) - time.Sleep(time.Second) - continue + return nil, fmt.Errorf("failed to fetch balance: %w", err) } if senderBal.Cmp(transferAmount) < 0 { return nil, fmt.Errorf("not enough balance %s to transfer %s", senderBal, transferAmount) } - cctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - nonce, err := ec.FetchNonce(cctx, sender) - cancel() + nonce, err := ec.FetchNonce(ctx, sender) if err != nil { return nil, err } @@ -153,34 +149,23 @@ func (ec *EvmClient) TransferTx( GasFeeCap: ec.feeCap, GasTipCap: ec.priorityFee, Value: transferAmount, - Data: []byte{}, }), ec.signer, senderPriv, ) if err != nil { - log.Printf("failed to sign transaction: %v (key address %s)", err, sender) - time.Sleep(time.Second) - continue + return nil, fmt.Errorf("failed to sign transaction: %w", err) } if err := ec.ethClient.SendTransaction(ctx, signedTx); err != nil { log.Printf("failed to send transaction: %v (key address %s)", err, sender) - - if strings.Contains(err.Error(), vmerrs.ErrSenderAddressNotAllowListed.Error()) { - return nil, err - } - - time.Sleep(time.Second) - continue + return nil, err } txHash := signedTx.Hash() cost, err := ec.ConfirmTx(ctx, txHash) if err != nil { - log.Printf("failed to confirm %s: %v", txHash.Hex(), err) - time.Sleep(time.Second) - continue + return nil, err } senderBal = new(big.Int).Sub(senderBal, cost) diff --git a/tests/utils/subnet.go b/tests/utils/subnet.go new file mode 100644 index 0000000000..75fb42f585 --- /dev/null +++ b/tests/utils/subnet.go @@ -0,0 +1,110 @@ +// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "context" + "encoding/json" + "fmt" + "os" + "os/exec" + "time" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + wallet "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/subnet-evm/core" + "github.com/ava-labs/subnet-evm/plugin/evm" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/onsi/gomega" +) + +func RunHardhatTests(test string, rpcURI string) { + log.Info("Sleeping to wait for test ping", "rpcURI", rpcURI) + client, err := NewEvmClient(rpcURI, 225, 2) + gomega.Expect(err).Should(gomega.BeNil()) + + bal, err := client.FetchBalance(context.Background(), common.HexToAddress("")) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(bal.Cmp(common.Big0)).Should(gomega.Equal(0)) + + err = os.Setenv("RPC_URI", rpcURI) + gomega.Expect(err).Should(gomega.BeNil()) + cmd := exec.Command("npx", "hardhat", "test", fmt.Sprintf("./test/%s.ts", test), "--network", "local") + cmd.Dir = "./contract-examples" + log.Info("Running hardhat command", "cmd", cmd.String()) + + out, err := cmd.CombinedOutput() + fmt.Printf("\nCombined output:\n\n%s\n", string(out)) + if err != nil { + fmt.Printf("\nErr: %s\n", err.Error()) + } + gomega.Expect(err).Should(gomega.BeNil()) +} + +func CreateNewSubnet(ctx context.Context, genesisFilePath string) string { + kc := secp256k1fx.NewKeychain(genesis.EWOQKey) + + // NewWalletFromURI fetches the available UTXOs owned by [kc] on the network + // that [LocalAPIURI] is hosting. + wallet, err := wallet.NewWalletFromURI(ctx, DefaultLocalNodeURI, kc) + gomega.Expect(err).Should(gomega.BeNil()) + + pWallet := wallet.P() + + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + genesis.EWOQKey.PublicKey().Address(), + }, + } + + wd, err := os.Getwd() + gomega.Expect(err).Should(gomega.BeNil()) + log.Info("Reading genesis file", "filePath", genesisFilePath, "wd", wd) + genesisBytes, err := os.ReadFile(genesisFilePath) + gomega.Expect(err).Should(gomega.BeNil()) + + log.Info("Creating new subnet") + createSubnetTxID, err := pWallet.IssueCreateSubnetTx(owner) + gomega.Expect(err).Should(gomega.BeNil()) + + genesis := &core.Genesis{} + err = json.Unmarshal(genesisBytes, genesis) + gomega.Expect(err).Should(gomega.BeNil()) + + log.Info("Creating new Subnet-EVM blockchain", "genesis", genesis) + createChainTxID, err := pWallet.IssueCreateChainTx( + createSubnetTxID, + genesisBytes, + evm.ID, + nil, + "testChain", + ) + gomega.Expect(err).Should(gomega.BeNil()) + + // Confirm the new blockchain is ready by waiting for the readiness endpoint + infoClient := info.NewClient(DefaultLocalNodeURI) + bootstrapped, err := info.AwaitBootstrapped(ctx, infoClient, createChainTxID.String(), 2*time.Second) + gomega.Expect(err).Should(gomega.BeNil()) + gomega.Expect(bootstrapped).Should(gomega.BeTrue()) + + // Return the blockchainID of the newly created blockchain + return createChainTxID.String() +} + +func ExecuteHardHatTestOnNewBlockchain(ctx context.Context, test string) { + log.Info("Executing HardHat tests on a new blockchain", "test", test) + + genesisFilePath := fmt.Sprintf("./tests/precompile/genesis/%s.json", test) + + blockchainID := CreateNewSubnet(ctx, genesisFilePath) + chainURI := fmt.Sprintf("%s/ext/bc/%s/rpc", DefaultLocalNodeURI, blockchainID) + + log.Info("Created subnet successfully", "ChainURI", chainURI) + RunHardhatTests(test, chainURI) +} diff --git a/trie/database.go b/trie/database.go index 25c76e0825..1436a37f6e 100644 --- a/trie/database.go +++ b/trie/database.go @@ -702,6 +702,7 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H nodes, storage := len(db.dirties), db.dirtiesSize toFlush, err := db.commit(node, make([]*flushItem, 0, 128), callback) if err != nil { + db.lock.RUnlock() log.Error("Failed to commit trie from trie database", "err", err) return err } diff --git a/warp/backend.go b/warp/backend.go new file mode 100644 index 0000000000..8cfac0b54e --- /dev/null +++ b/warp/backend.go @@ -0,0 +1,92 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/cache" + "github.com/ava-labs/avalanchego/database" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/vms/platformvm/teleporter" +) + +var _ WarpBackend = &warpBackend{} + +// WarpBackend tracks signature eligible warp messages and provides an interface to fetch them. +// The backend is also used to query for warp message signatures by the signature request handler. +type WarpBackend interface { + // AddMessage signs [unsignedMessage] and adds it to the warp backend database + AddMessage(ctx context.Context, unsignedMessage *teleporter.UnsignedMessage) error + + // GetSignature returns the signature of the requested message hash. + GetSignature(ctx context.Context, messageHash ids.ID) ([bls.SignatureLen]byte, error) +} + +// warpBackend implements WarpBackend, keeps track of warp messages, and generates message signatures. +type warpBackend struct { + db database.Database + snowCtx *snow.Context + signatureCache *cache.LRU[ids.ID, [bls.SignatureLen]byte] +} + +// NewWarpBackend creates a new WarpBackend, and initializes the signature cache and message tracking database. +func NewWarpBackend(snowCtx *snow.Context, db database.Database, signatureCacheSize int) WarpBackend { + return &warpBackend{ + db: db, + snowCtx: snowCtx, + signatureCache: &cache.LRU[ids.ID, [bls.SignatureLen]byte]{Size: signatureCacheSize}, + } +} + +func (w *warpBackend) AddMessage(ctx context.Context, unsignedMessage *teleporter.UnsignedMessage) error { + messageID := hashing.ComputeHash256Array(unsignedMessage.Bytes()) + + // In the case when a node restarts, and possibly changes its bls key, the cache gets emptied but the database does not. + // So to avoid having incorrect signatures saved in the database after a bls key change, we save the full message in the database. + // Whereas for the cache, after the node restart, the cache would be emptied so we can directly save the signatures. + if err := w.db.Put(messageID[:], unsignedMessage.Bytes()); err != nil { + return fmt.Errorf("failed to put warp signature in db: %w", err) + } + + var signature [bls.SignatureLen]byte + sig, err := w.snowCtx.TeleporterSigner.Sign(unsignedMessage) + if err != nil { + return fmt.Errorf("failed to sign warp message: %w", err) + } + + copy(signature[:], sig) + w.signatureCache.Put(messageID, signature) + return nil +} + +func (w *warpBackend) GetSignature(ctx context.Context, messageID ids.ID) ([bls.SignatureLen]byte, error) { + if sig, ok := w.signatureCache.Get(messageID); ok { + return sig, nil + } + + unsignedMessageBytes, err := w.db.Get(messageID[:]) + if err != nil { + return [bls.SignatureLen]byte{}, fmt.Errorf("failed to get warp message %s from db: %w", messageID.String(), err) + } + + unsignedMessage, err := teleporter.ParseUnsignedMessage(unsignedMessageBytes) + if err != nil { + return [bls.SignatureLen]byte{}, fmt.Errorf("failed to parse unsigned message %s: %w", messageID.String(), err) + } + + var signature [bls.SignatureLen]byte + sig, err := w.snowCtx.TeleporterSigner.Sign(unsignedMessage) + if err != nil { + return [bls.SignatureLen]byte{}, fmt.Errorf("failed to sign warp message: %w", err) + } + + copy(signature[:], sig) + w.signatureCache.Put(messageID, signature) + return signature, nil +} diff --git a/warp/backend_test.go b/warp/backend_test.go new file mode 100644 index 0000000000..c4449245f5 --- /dev/null +++ b/warp/backend_test.go @@ -0,0 +1,88 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "testing" + + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/vms/platformvm/teleporter" + "github.com/stretchr/testify/require" +) + +var ( + sourceChainID = ids.GenerateTestID() + destinationChainID = ids.GenerateTestID() + payload = []byte("test") +) + +func TestAddAndGetValidMessage(t *testing.T) { + db := memdb.New() + + snowCtx := snow.DefaultContextTest() + sk, err := bls.NewSecretKey() + require.NoError(t, err) + snowCtx.TeleporterSigner = teleporter.NewSigner(sk, sourceChainID) + backend := NewWarpBackend(snowCtx, db, 500) + + // Create a new unsigned message and add it to the warp backend. + unsignedMsg, err := teleporter.NewUnsignedMessage(sourceChainID, destinationChainID, payload) + require.NoError(t, err) + err = backend.AddMessage(context.Background(), unsignedMsg) + require.NoError(t, err) + + // Verify that a signature is returned successfully, and compare to expected signature. + messageID := hashing.ComputeHash256Array(unsignedMsg.Bytes()) + signature, err := backend.GetSignature(context.Background(), messageID) + require.NoError(t, err) + + expectedSig, err := snowCtx.TeleporterSigner.Sign(unsignedMsg) + require.NoError(t, err) + require.Equal(t, expectedSig, signature[:]) +} + +func TestAddAndGetUnknownMessage(t *testing.T) { + db := memdb.New() + + backend := NewWarpBackend(snow.DefaultContextTest(), db, 500) + unsignedMsg, err := teleporter.NewUnsignedMessage(sourceChainID, destinationChainID, payload) + require.NoError(t, err) + + // Try getting a signature for a message that was not added. + messageID := hashing.ComputeHash256Array(unsignedMsg.Bytes()) + _, err = backend.GetSignature(context.Background(), messageID) + require.Error(t, err) +} + +func TestZeroSizedCache(t *testing.T) { + db := memdb.New() + + snowCtx := snow.DefaultContextTest() + sk, err := bls.NewSecretKey() + require.NoError(t, err) + snowCtx.TeleporterSigner = teleporter.NewSigner(sk, sourceChainID) + + // Verify zero sized cache works normally, because the lru cache will be initialized to size 1 for any size parameter <= 0. + backend := NewWarpBackend(snowCtx, db, 0) + + // Create a new unsigned message and add it to the warp backend. + unsignedMsg, err := teleporter.NewUnsignedMessage(sourceChainID, destinationChainID, payload) + require.NoError(t, err) + err = backend.AddMessage(context.Background(), unsignedMsg) + require.NoError(t, err) + + // Verify that a signature is returned successfully, and compare to expected signature. + messageID := hashing.ComputeHash256Array(unsignedMsg.Bytes()) + signature, err := backend.GetSignature(context.Background(), messageID) + require.NoError(t, err) + + expectedSig, err := snowCtx.TeleporterSigner.Sign(unsignedMsg) + require.NoError(t, err) + require.Equal(t, expectedSig, signature[:]) +} diff --git a/warp/handlers/signature_request.go b/warp/handlers/signature_request.go new file mode 100644 index 0000000000..6cf28efd74 --- /dev/null +++ b/warp/handlers/signature_request.go @@ -0,0 +1,75 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package handlers + +import ( + "context" + "time" + + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ava-labs/subnet-evm/warp" + "github.com/ava-labs/subnet-evm/warp/handlers/stats" + "github.com/ethereum/go-ethereum/log" +) + +// SignatureRequestHandler is a peer.RequestHandler for message.SignatureRequest +// serving requested BLS signature data +type SignatureRequestHandler interface { + OnSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest message.SignatureRequest) ([]byte, error) +} + +// signatureRequestHandler implements the SignatureRequestHandler interface +type signatureRequestHandler struct { + backend warp.WarpBackend + codec codec.Manager + stats stats.SignatureRequestHandlerStats +} + +func NewSignatureRequestHandler(backend warp.WarpBackend, codec codec.Manager, stats stats.SignatureRequestHandlerStats) SignatureRequestHandler { + return &signatureRequestHandler{ + backend: backend, + codec: codec, + stats: stats, + } +} + +// OnSignatureRequest handles message.SignatureRequest, and retrieves a warp signature for the requested message ID. +// Never returns an error +// Expects returned errors to be treated as FATAL +// Returns empty response if signature is not found +// Assumes ctx is active +func (s *signatureRequestHandler) OnSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest message.SignatureRequest) ([]byte, error) { + startTime := time.Now() + s.stats.IncSignatureRequest() + + // Always report signature request time + defer func() { + s.stats.UpdateSignatureRequestTime(time.Since(startTime)) + }() + + signature, err := s.backend.GetSignature(ctx, signatureRequest.MessageID) + if err != nil { + log.Debug("Unknown warp signature requested", "messageID", signatureRequest.MessageID) + s.stats.IncSignatureMiss() + return nil, nil + } + + s.stats.IncSignatureHit() + response := message.SignatureResponse{Signature: signature} + responseBytes, err := s.codec.Marshal(message.Version, &response) + if err != nil { + log.Warn("could not marshal SignatureResponse, dropping request", "nodeID", nodeID, "requestID", requestID, "err", err) + return nil, nil + } + + return responseBytes, nil +} + +type NoopSignatureRequestHandler struct{} + +func (s *NoopSignatureRequestHandler) OnSignatureRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, signatureRequest message.SignatureRequest) ([]byte, error) { + return nil, nil +} diff --git a/warp/handlers/signature_request_test.go b/warp/handlers/signature_request_test.go new file mode 100644 index 0000000000..fa2d53664d --- /dev/null +++ b/warp/handlers/signature_request_test.go @@ -0,0 +1,99 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package handlers + +import ( + "context" + "testing" + "time" + + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/vms/platformvm/teleporter" + "github.com/ava-labs/subnet-evm/plugin/evm/message" + "github.com/ava-labs/subnet-evm/warp" + "github.com/ava-labs/subnet-evm/warp/handlers/stats" + "github.com/stretchr/testify/require" +) + +func TestSignatureHandler(t *testing.T) { + database := memdb.New() + snowCtx := snow.DefaultContextTest() + blsSecretKey, err := bls.NewSecretKey() + require.NoError(t, err) + + snowCtx.TeleporterSigner = teleporter.NewSigner(blsSecretKey, snowCtx.ChainID) + warpBackend := warp.NewWarpBackend(snowCtx, database, 100) + + msg, err := teleporter.NewUnsignedMessage(snowCtx.ChainID, snowCtx.CChainID, []byte("test")) + require.NoError(t, err) + + messageID := hashing.ComputeHash256Array(msg.Bytes()) + require.NoError(t, warpBackend.AddMessage(context.Background(), msg)) + signature, err := warpBackend.GetSignature(context.Background(), messageID) + require.NoError(t, err) + unknownMessageID := ids.GenerateTestID() + + mockHandlerStats := &stats.MockSignatureRequestHandlerStats{} + signatureRequestHandler := NewSignatureRequestHandler(warpBackend, message.Codec, mockHandlerStats) + + tests := map[string]struct { + setup func() (request message.SignatureRequest, expectedResponse []byte) + verifyStats func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) + }{ + "normal": { + setup: func() (request message.SignatureRequest, expectedResponse []byte) { + return message.SignatureRequest{ + MessageID: messageID, + }, signature[:] + }, + verifyStats: func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) { + require.EqualValues(t, 1, mockHandlerStats.SignatureRequestCount) + require.EqualValues(t, 1, mockHandlerStats.SignatureRequestHit) + require.EqualValues(t, 0, mockHandlerStats.SignatureRequestMiss) + require.Greater(t, mockHandlerStats.SignatureRequestDuration, time.Duration(0)) + }, + }, + "unknown": { + setup: func() (request message.SignatureRequest, expectedResponse []byte) { + return message.SignatureRequest{ + MessageID: unknownMessageID, + }, nil + }, + verifyStats: func(t *testing.T, stats *stats.MockSignatureRequestHandlerStats) { + require.EqualValues(t, 1, mockHandlerStats.SignatureRequestCount) + require.EqualValues(t, 1, mockHandlerStats.SignatureRequestMiss) + require.EqualValues(t, 0, mockHandlerStats.SignatureRequestHit) + require.Greater(t, mockHandlerStats.SignatureRequestDuration, time.Duration(0)) + }, + }, + } + + for name, test := range tests { + // Reset stats before each test + mockHandlerStats.Reset() + + t.Run(name, func(t *testing.T) { + request, expectedResponse := test.setup() + responseBytes, err := signatureRequestHandler.OnSignatureRequest(context.Background(), ids.GenerateTestNodeID(), 1, request) + require.NoError(t, err) + + // If the expected response is empty, assert that the handler returns an empty response and return early. + if len(expectedResponse) == 0 { + test.verifyStats(t, mockHandlerStats) + require.Len(t, responseBytes, 0, "expected response to be empty") + return + } + var response message.SignatureResponse + _, err = message.Codec.Unmarshal(responseBytes, &response) + require.NoError(t, err, "error unmarshalling SignatureResponse") + + require.Equal(t, expectedResponse, response.Signature[:]) + test.verifyStats(t, mockHandlerStats) + }) + } +} diff --git a/warp/handlers/stats/stats.go b/warp/handlers/stats/stats.go new file mode 100644 index 0000000000..2cc593667e --- /dev/null +++ b/warp/handlers/stats/stats.go @@ -0,0 +1,95 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package stats + +import ( + "sync" + "time" + + "github.com/ava-labs/subnet-evm/metrics" +) + +var ( + _ SignatureRequestHandlerStats = (*handlerStats)(nil) + _ SignatureRequestHandlerStats = (*MockSignatureRequestHandlerStats)(nil) +) + +type SignatureRequestHandlerStats interface { + IncSignatureRequest() + IncSignatureHit() + IncSignatureMiss() + UpdateSignatureRequestTime(duration time.Duration) +} + +type handlerStats struct { + // SignatureRequestHandler metrics + signatureRequest metrics.Counter + signatureHit metrics.Counter + signatureMiss metrics.Counter + signatureProcessingTime metrics.Timer +} + +func NewStats(enabled bool) SignatureRequestHandlerStats { + if !enabled { + return &MockSignatureRequestHandlerStats{} + } + + return &handlerStats{ + signatureRequest: metrics.GetOrRegisterCounter("signature_request_count", nil), + signatureHit: metrics.GetOrRegisterCounter("signature_request_hit", nil), + signatureMiss: metrics.GetOrRegisterCounter("signature_request_miss", nil), + signatureProcessingTime: metrics.GetOrRegisterTimer("signature_request_duration", nil), + } +} + +func (h *handlerStats) IncSignatureRequest() { h.signatureRequest.Inc(1) } +func (h *handlerStats) IncSignatureHit() { h.signatureHit.Inc(1) } +func (h *handlerStats) IncSignatureMiss() { h.signatureMiss.Inc(1) } +func (h *handlerStats) UpdateSignatureRequestTime(duration time.Duration) { + h.signatureProcessingTime.Update(duration) +} + +// MockSignatureRequestHandlerStats is mock for capturing and asserting on handler metrics in test +type MockSignatureRequestHandlerStats struct { + lock sync.Mutex + + SignatureRequestCount, + SignatureRequestHit, + SignatureRequestMiss uint32 + SignatureRequestDuration time.Duration +} + +func (m *MockSignatureRequestHandlerStats) Reset() { + m.lock.Lock() + defer m.lock.Unlock() + + m.SignatureRequestCount = 0 + m.SignatureRequestHit = 0 + m.SignatureRequestMiss = 0 + m.SignatureRequestDuration = 0 +} + +func (m *MockSignatureRequestHandlerStats) IncSignatureRequest() { + m.lock.Lock() + defer m.lock.Unlock() + m.SignatureRequestCount++ +} + +func (m *MockSignatureRequestHandlerStats) IncSignatureHit() { + m.lock.Lock() + defer m.lock.Unlock() + m.SignatureRequestHit++ +} + +func (m *MockSignatureRequestHandlerStats) IncSignatureMiss() { + m.lock.Lock() + defer m.lock.Unlock() + m.SignatureRequestMiss++ +} + +func (m *MockSignatureRequestHandlerStats) UpdateSignatureRequestTime(duration time.Duration) { + m.lock.Lock() + defer m.lock.Unlock() + m.SignatureRequestDuration += duration +} diff --git a/warp/warp_client.go b/warp/warp_client.go new file mode 100644 index 0000000000..bfa4638efb --- /dev/null +++ b/warp/warp_client.go @@ -0,0 +1,45 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/rpc" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ WarpClient = (*warpClient)(nil) + +type WarpClient interface { + GetSignature(ctx context.Context, messageID ids.ID) ([]byte, error) +} + +// warpClient implementation for interacting with EVM [chain] +type warpClient struct { + client *rpc.Client +} + +// NewWarpClient returns a WarpClient for interacting with EVM [chain] +func NewWarpClient(uri, chain string) (WarpClient, error) { + client, err := rpc.Dial(fmt.Sprintf("%s/ext/bc/%s/rpc", uri, chain)) + if err != nil { + return nil, fmt.Errorf("failed to dial client. err: %w", err) + } + return &warpClient{ + client: client, + }, nil +} + +// GetSignature requests the BLS signature associated with a messageID +func (c *warpClient) GetSignature(ctx context.Context, messageID ids.ID) ([]byte, error) { + var res hexutil.Bytes + err := c.client.CallContext(ctx, &res, "warp_getSignature", messageID) + if err != nil { + return nil, fmt.Errorf("call to warp_getSignature failed. err: %w", err) + } + return res, err +} diff --git a/warp/warp_service.go b/warp/warp_service.go new file mode 100644 index 0000000000..3e2a003207 --- /dev/null +++ b/warp/warp_service.go @@ -0,0 +1,26 @@ +// (c) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package warp + +import ( + "context" + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// WarpAPI introduces snowman specific functionality to the evm +type WarpAPI struct { + Backend WarpBackend +} + +// GetSignature returns the BLS signature associated with a messageID. +func (api *WarpAPI) GetSignature(ctx context.Context, messageID ids.ID) (hexutil.Bytes, error) { + signature, err := api.Backend.GetSignature(ctx, messageID) + if err != nil { + return nil, fmt.Errorf("failed to get signature for with error %w", err) + } + return signature[:], nil +}